]> granicus.if.org Git - esp-idf/commitdiff
tools: implement linker script generation
authorRenz Bagaporo <renz@espressif.com>
Wed, 18 Apr 2018 02:57:45 +0000 (10:57 +0800)
committerRenz Christian Bagaporo <renz@espressif.com>
Fri, 16 Nov 2018 04:42:02 +0000 (12:42 +0800)
62 files changed:
.gitlab-ci.yml
components/app_trace/CMakeLists.txt
components/app_trace/component.mk
components/app_trace/linker.lf [new file with mode: 0644]
components/esp32/CMakeLists.txt
components/esp32/Makefile.projbuild
components/esp32/component.mk
components/esp32/ld/esp32.common.ld.in [moved from components/esp32/ld/esp32.common.ld with 84% similarity]
components/esp32/ld/esp32_fragments.lf [new file with mode: 0644]
components/esp32/linker.lf [new file with mode: 0644]
components/esp_ringbuf/CMakeLists.txt
components/esp_ringbuf/component.mk
components/esp_ringbuf/linker.lf [new file with mode: 0644]
components/freertos/CMakeLists.txt
components/freertos/component.mk
components/freertos/linker.lf [new file with mode: 0644]
components/heap/CMakeLists.txt
components/heap/component.mk
components/heap/linker.lf [new file with mode: 0644]
components/soc/CMakeLists.txt
components/soc/component.mk
components/soc/linker.lf [new file with mode: 0644]
components/spi_flash/CMakeLists.txt
components/spi_flash/component.mk
components/spi_flash/linker.lf [new file with mode: 0644]
components/xtensa-debug-module/CMakeLists.txt
components/xtensa-debug-module/component.mk
components/xtensa-debug-module/linker.lf [new file with mode: 0644]
docs/en/api-guides/build-system-cmake.rst
docs/en/api-guides/build-system.rst
docs/en/api-guides/index.rst
docs/en/api-guides/linker-script-generation.rst [new file with mode: 0644]
docs/zh_CN/api-guides/index.rst
docs/zh_CN/api-guides/linker-script-generation.rst [new file with mode: 0644]
make/component_wrapper.mk
make/ldgen.mk [new file with mode: 0644]
make/project.mk
tools/ci/executable-list.txt
tools/ci/test_build_system.sh
tools/ci/test_build_system_cmake.sh
tools/cmake/components.cmake
tools/cmake/ldgen.cmake [new file with mode: 0644]
tools/cmake/project.cmake
tools/cmake/scripts/expand_requirements.cmake
tools/ldgen/__init__.py [new file with mode: 0644]
tools/ldgen/fragments.py [new file with mode: 0644]
tools/ldgen/generation.py [new file with mode: 0644]
tools/ldgen/ldgen.py [new file with mode: 0755]
tools/ldgen/pyparsing.py [new file with mode: 0644]
tools/ldgen/samples/esp32.lf [new file with mode: 0644]
tools/ldgen/samples/mappings.lf [new file with mode: 0644]
tools/ldgen/samples/sdkconfig [new file with mode: 0644]
tools/ldgen/samples/sections.info [new file with mode: 0644]
tools/ldgen/samples/template.ld [new file with mode: 0644]
tools/ldgen/sdkconfig.py [new file with mode: 0644]
tools/ldgen/test/data/Kconfig [new file with mode: 0644]
tools/ldgen/test/data/sample.lf [new file with mode: 0644]
tools/ldgen/test/data/sdkconfig [new file with mode: 0644]
tools/ldgen/test/data/sections.info [new file with mode: 0644]
tools/ldgen/test/data/template.ld [new file with mode: 0644]
tools/ldgen/test/test_fragments.py [new file with mode: 0755]
tools/ldgen/test/test_generation.py [new file with mode: 0755]

index 73c2cf577e8fdfcf13374c1428c9072f6c70bb40..4bfea8f6471a0c2e62cd53ba2652e0fe21e4dd15 100644 (file)
@@ -441,6 +441,13 @@ test_fatfs_on_host:
     - cd components/fatfs/test_fatfs_host/
     - make test
 
+test_ldgen_on_host:
+  <<: *host_test_template
+  script:
+    - cd tools/ldgen/test
+    - ./test_fragments.py
+    - ./test_generation.py
+
 .host_fuzzer_test_template: &host_fuzzer_test_template
   stage: host_test
   image: $CI_DOCKER_REGISTRY/afl-fuzzer-test
index 151015021a7d0ac7d52ffb41f4e16c6c5c01c619..ffd60950986a910dfaf32e4b24c14aa6a3c86930 100644 (file)
@@ -18,6 +18,7 @@ endif()
 
 set(COMPONENT_REQUIRES)
 set(COMPONENT_PRIV_REQUIRES xtensa-debug-module)
+set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
 
 register_component()
 
index b53e27d307ee0ac9bb3271d03b031a45fdc1e97f..bfe77da43a1faf3c27dafe355c091ac625de40e4 100644 (file)
@@ -26,4 +26,6 @@ COMPONENT_SRCDIRS += \
        sys_view/esp32
 else
 COMPONENT_SRCDIRS += gcov
+
+COMPONENT_ADD_LDFRAGMENTS += linker.lf
 endif
diff --git a/components/app_trace/linker.lf b/components/app_trace/linker.lf
new file mode 100644 (file)
index 0000000..d615324
--- /dev/null
@@ -0,0 +1,5 @@
+[mapping]
+archive: libapp_trace.a
+entries: 
+    * (noflash)
+
index daa000c4f11eaa8ef1cede2a3b209e9fe6c6ff27..880278508e3344694655bc4f767eecaa53010d2d 100644 (file)
@@ -71,6 +71,8 @@ else()
         app_trace bootloader_support ethernet log mbedtls nvs_flash
         pthread smartconfig_ack spi_flash vfs wpa_supplicant xtensa-debug-module)
 
+    set(COMPONENT_ADD_LDFRAGMENTS linker.lf ld/esp32_fragments.lf)
+
     register_component()
 
     target_link_libraries(esp32 "-L ${CMAKE_CURRENT_SOURCE_DIR}/lib")
@@ -84,7 +86,16 @@ else()
         target_linker_script(esp32 "ld/esp32.extram.bss.ld")
     endif()
 
-    target_linker_script(esp32 "ld/esp32.common.ld")
+    # Process the template file through the linker script generation mechanism, and use the output for linking the
+    # final binary
+    set(esp32_common_script "${CMAKE_CURRENT_BINARY_DIR}/esp32.common.ld")
+    set(esp32_common_template "${CMAKE_CURRENT_LIST_DIR}/ld/esp32.common.ld.in")
+
+    ldgen_process_template(${esp32_common_template} ${esp32_common_script})
+
+    target_link_libraries(esp32 "-T ${esp32_common_script}")
+
+    set_property(TARGET ${PROJECT_NAME}.elf APPEND PROPERTY LINK_DEPENDS ${esp32_common_script})
 
     target_linker_script(esp32
         "ld/esp32.rom.ld"
@@ -146,7 +157,7 @@ else()
 
     endif()
 
-    # Enable dynamic esp_timer overflow value if building unit tests 
+    # Enable dynamic esp_timer overflow value if building unit tests
     if(NOT "${BUILD_TEST_COMPONENTS}" EQUAL "")
         add_definitions(-DESP_TIMER_DYNAMIC_OVERFLOW_VAL)
     endif()
index 9a51d80b3ee85542b23bcb2575eb5dd33c0eb7a9..a17591ff01249ba6803ba71e2e9c2094b2322722 100644 (file)
@@ -42,3 +42,9 @@ endif
 ifneq ("$(TEST_COMPONENTS_LIST)","")
 CPPFLAGS += -DESP_TIMER_DYNAMIC_OVERFLOW_VAL
 endif
+ESP32_LINKER_SCRIPT_TEMPLATE := $(COMPONENT_PATH)/ld/esp32.common.ld.in
+ESP32_LINKER_SCRIPT_OUTPUT_DIR := $(abspath $(BUILD_DIR_BASE)/esp32)
+
+# Target to generate linker script generator from fragments presented by each of
+# the components
+$(eval $(call ldgen_process_template, $(ESP32_LINKER_SCRIPT_TEMPLATE), $(ESP32_LINKER_SCRIPT_OUTPUT_DIR)/esp32.common.ld))
index 605bb5637348256008f008d9678befd7f2517a9e..54d4a2a5c537ed38da786e64c9b2939efc0fc7a5 100644 (file)
@@ -16,7 +16,7 @@ endif
 #Linker scripts used to link the final application.
 #Warning: These linker scripts are only used when the normal app is compiled; the bootloader
 #specifies its own scripts.
-LINKER_SCRIPTS += esp32.common.ld esp32.rom.ld esp32.peripherals.ld
+LINKER_SCRIPTS += $(COMPONENT_BUILD_DIR)/esp32.common.ld esp32.rom.ld esp32.peripherals.ld
 
 #Force pure functions from libgcc.a to be linked from ROM
 LINKER_SCRIPTS += esp32.rom.libgcc.ld
@@ -35,7 +35,7 @@ ifndef CONFIG_SPI_FLASH_ROM_DRIVER_PATCH
 LINKER_SCRIPTS += esp32.rom.spiflash.ld
 endif
 
-#ld_include_panic_highint_hdl is added as an undefined symbol because otherwise the 
+#ld_include_panic_highint_hdl is added as an undefined symbol because otherwise the
 #linker will ignore panic_highint_hdl.S as it has no other files depending on any
 #symbols in it.
 COMPONENT_ADD_LDFLAGS += $(COMPONENT_PATH)/libhal.a \
@@ -44,7 +44,9 @@ COMPONENT_ADD_LDFLAGS += $(COMPONENT_PATH)/libhal.a \
                          -L $(COMPONENT_PATH)/ld \
                          -T esp32_out.ld \
                          -u ld_include_panic_highint_hdl \
-                         $(addprefix -T ,$(LINKER_SCRIPTS))
+                         $(addprefix -T ,$(LINKER_SCRIPTS)) \
+
+COMPONENT_ADD_LDFRAGMENTS += ld/esp32_fragments.lf linker.lf
 
 ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS))
 
@@ -52,7 +54,9 @@ COMPONENT_SUBMODULES += lib
 
 # final linking of project ELF depends on all binary libraries, and
 # all linker scripts (except esp32_out.ld, as this is code generated here.)
-COMPONENT_ADD_LINKER_DEPS := $(ALL_LIB_FILES) $(addprefix ld/,$(LINKER_SCRIPTS))
+COMPONENT_ADD_LINKER_DEPS := $(ALL_LIB_FILES) \
+                            $(addprefix ld/, $(filter-out $(COMPONENT_BUILD_DIR)/esp32.common.ld, $(LINKER_SCRIPTS))) \
+                            $(COMPONENT_BUILD_DIR)/esp32.common.ld
 
 # Preprocess esp32.ld linker script into esp32_out.ld
 #
@@ -63,7 +67,7 @@ $(COMPONENT_LIBRARY): esp32_out.ld
 esp32_out.ld: $(COMPONENT_PATH)/ld/esp32.ld ../include/sdkconfig.h
        $(CC) -I ../include -C -P -x c -E $< -o $@
 
-COMPONENT_EXTRA_CLEAN := esp32_out.ld
+COMPONENT_EXTRA_CLEAN := esp32_out.ld $(COMPONENT_BUILD_DIR)/esp32.common.ld
 
 # disable stack protection in files which are involved in initialization of that feature
 stack_check.o: CFLAGS := $(filter-out -fstack-protector%, $(CFLAGS))
similarity index 84%
rename from components/esp32/ld/esp32.common.ld
rename to components/esp32/ld/esp32.common.ld.in
index 744fbc211659884e0086dd4c2ec22b830d1b1bc4..a05d829830fa30a3927f82babac33cd436553f0b 100644 (file)
@@ -9,7 +9,9 @@ SECTIONS
   .rtc.text :
   {
     . = ALIGN(4);
-    *(.rtc.literal .rtc.text)
+
+    mapping[rtc_text]
+
     *rtc_wake_stub*.*(.literal .text .literal.* .text.*)
     _rtc_text_end = ABSOLUTE(.);
   } > rtc_iram_seg
@@ -49,8 +51,9 @@ SECTIONS
   .rtc.data :
   {
     _rtc_data_start = ABSOLUTE(.);
-    *(.rtc.data)
-    *(.rtc.rodata)
+
+    mapping[rtc_data]
+
     *rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .bss .bss.*)
     _rtc_data_end = ABSOLUTE(.);
   } > rtc_data_location
@@ -61,7 +64,9 @@ SECTIONS
     _rtc_bss_start = ABSOLUTE(.);
     *rtc_wake_stub*.*(.bss .bss.*)
     *rtc_wake_stub*.*(COMMON)
-    *(.rtc.bss)
+
+    mapping[rtc_bss]
+
     _rtc_bss_end = ABSOLUTE(.);
   } > rtc_data_location
 
@@ -152,22 +157,9 @@ SECTIONS
   {
     /* Code marked as runnning out of IRAM */
     _iram_text_start = ABSOLUTE(.);
-    *(.iram1 .iram1.*)
-    *libesp_ringbuf.a:(.literal .text .literal.* .text.*)
-    *libfreertos.a:(.literal .text .literal.* .text.*)
-    *libheap.a:multi_heap.*(.literal .text .literal.* .text.*)
-    *libheap.a:multi_heap_poisoning.*(.literal .text .literal.* .text.*)
-    *libesp32.a:panic.*(.literal .text .literal.* .text.*)
-    *libesp32.a:core_dump.*(.literal .text .literal.* .text.*)
-    *libapp_trace.a:(.literal .text .literal.* .text.*)
-    *libxtensa-debug-module.a:eri.*(.literal .text .literal.* .text.*)
-    *librtc.a:(.literal .text .literal.* .text.*)
-    *libsoc.a:rtc_*.*(.literal .text .literal.* .text.*)
-    *libsoc.a:cpu_util.*(.literal .text .literal.* .text.*)
-    *libhal.a:(.literal .text .literal.* .text.*)
-    *libgcc.a:lib2funcs.*(.literal .text .literal.* .text.*)
-    *libspi_flash.a:spi_flash_rom_patch.*(.literal .text .literal.* .text.*)
-    *libgcov.a:(.literal .text .literal.* .text.*)
+
+    mapping[iram0_text]
+    
     INCLUDE esp32.spiram.rom-functions-iram.ld
     _iram_text_end = ABSOLUTE(.);
     _iram_end = ABSOLUTE(.);
@@ -187,8 +179,6 @@ SECTIONS
     *libbtdm_app.a:(.data .data.*)
     . = ALIGN (4);
     _btdm_data_end = ABSOLUTE(.);
-    *(.data)
-    *(.data.*)
     *(.gnu.linkonce.d.*)
     *(.data1)
     *(.sdata)
@@ -198,14 +188,9 @@ SECTIONS
     *(.sdata2.*)
     *(.gnu.linkonce.s2.*)
     *(.jcr)
-    *(.dram1 .dram1.*)
-    *libesp32.a:panic.*(.rodata .rodata.*)
-    *libphy.a:(.rodata .rodata.*)
-    *libsoc.a:rtc_clk.*(.rodata .rodata.*)
-    *libapp_trace.a:(.rodata .rodata.*)
-    *libgcov.a:(.rodata .rodata.*)
-    *libheap.a:multi_heap.*(.rodata .rodata.*)
-    *libheap.a:multi_heap_poisoning.*(.rodata .rodata.*)
+
+    mapping[dram0_data]
+
     INCLUDE esp32.spiram.rom-functions-dram.ld
     _data_end = ABSOLUTE(.);
     . = ALIGN(4);
@@ -239,6 +224,9 @@ SECTIONS
     *libbtdm_app.a:(.bss .bss.* COMMON)
     . = ALIGN (4);
     _btdm_bss_end = ABSOLUTE(.);
+
+    mapping[dram0_bss]
+
     *(.dynsbss)
     *(.sbss)
     *(.sbss.*)
@@ -248,11 +236,9 @@ SECTIONS
     *(.sbss2.*)
     *(.gnu.linkonce.sb2.*)
     *(.dynbss)
-    *(.bss)
-    *(.bss.*)
     *(.share.mem)
     *(.gnu.linkonce.b.*)
-    *(COMMON)
+
     . = ALIGN (8);
     _bss_end = ABSOLUTE(.);
     /* The heap starts right after end of this section */
@@ -265,8 +251,9 @@ SECTIONS
   .flash.rodata :
   {
     _rodata_start = ABSOLUTE(.);
-    *(.rodata)
-    *(.rodata.*)
+
+    mapping[flash_rodata]
+
     *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
     *(.gnu.linkonce.r.*)
     *(.rodata1)
@@ -324,7 +311,10 @@ SECTIONS
   {
     _stext = .;
     _text_start = ABSOLUTE(.);
-    *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
+
+    mapping[flash_text]
+
+    *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
     *(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */
     *(.fini.literal)
     *(.fini)
diff --git a/components/esp32/ld/esp32_fragments.lf b/components/esp32/ld/esp32_fragments.lf
new file mode 100644 (file)
index 0000000..65063f5
--- /dev/null
@@ -0,0 +1,80 @@
+[sections:text]
+entries:
+    .text+
+    .literal+
+
+[sections:data]
+entries:
+    .data+
+
+[sections:bss]
+entries:
+    .bss+
+
+[sections:common]
+entries:
+    COMMON
+
+[sections:rodata]
+entries:
+    .rodata+
+
+[sections:rtc_text]
+entries:
+    .rtc.text
+    .rtc.literal
+
+[sections:rtc_data]
+entries:
+    .rtc.data
+
+[sections:rtc_rodata]
+entries:
+    .rtc.rodata
+
+[sections:rtc_bss]
+entries:
+    .rtc.bss
+
+[sections:iram]
+entries:
+    .iram1+
+
+[sections:dram]
+entries:
+    .dram1+
+
+[scheme:default]
+entries:
+    text -> flash_text
+    rodata -> flash_rodata
+    data -> dram0_data
+    bss -> dram0_bss
+    common -> dram0_bss
+    iram -> iram0_text
+    dram -> dram0_data
+    rtc_text -> rtc_text
+    rtc_data -> rtc_data
+    rtc_rodata -> rtc_data
+    rtc_bss -> rtc_bss
+
+[scheme:rtc]
+entries:
+    text -> rtc_text
+    data -> rtc_data
+    rodata -> rtc_data
+    bss -> rtc_bss
+    common -> rtc_bss
+
+[scheme:noflash]
+entries:
+    text -> iram0_text
+    rodata -> dram0_data
+
+[scheme:noflash_data]
+entries:
+    rodata -> dram0_data
+
+[scheme:noflash_text]
+entries:
+    text -> iram0_text
diff --git a/components/esp32/linker.lf b/components/esp32/linker.lf
new file mode 100644 (file)
index 0000000..53f4501
--- /dev/null
@@ -0,0 +1,30 @@
+[mapping]
+archive: libesp32.a
+entries: 
+    core_dump (noflash_text)
+    panic (noflash)
+
+[mapping]
+archive: libphy.a
+entries: 
+    * (noflash_data)
+
+[mapping]
+archive: libhal.a
+entries: 
+    * (noflash_text)
+
+[mapping]
+archive: librtc.a
+entries: 
+    * (noflash_text)
+
+[mapping]
+archive: libgcc.a
+entries: 
+    lib2funcs (noflash_text)
+
+[mapping]
+archive: libgcov.a
+entries: 
+    * (noflash)
index 9117521a279409b68b91f014c476d904b818585d..bba1dfd6d83e3aa014832e0c4470dc990665d887 100644 (file)
@@ -1,5 +1,6 @@
 set(COMPONENT_ADD_INCLUDEDIRS "include")
 set(COMPONENT_SRCS "ringbuf.c")
+set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
 
 set(COMPONENT_REQUIRES)
 
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cb3a3456bde284bb40a87bb09114a79981be7b79 100644 (file)
@@ -0,0 +1 @@
+COMPONENT_ADD_LDFRAGMENTS += linker.lf
diff --git a/components/esp_ringbuf/linker.lf b/components/esp_ringbuf/linker.lf
new file mode 100644 (file)
index 0000000..374aa4c
--- /dev/null
@@ -0,0 +1,4 @@
+[mapping]
+archive: libesp_ringbuf.a
+entries:
+    * (noflash_text)
\ No newline at end of file
index 1017fc310d387619c169368aac61c1455bed5a49..3b0886529f55dbe915af8283aadc0c50c964d876 100644 (file)
@@ -20,7 +20,7 @@ set(COMPONENT_SRCS "FreeRTOS-openocd.c"
 # app_trace is required by FreeRTOS headers only when CONFIG_SYSVIEW_ENABLE=y,
 # but requirements can't depend on config options, so always require it.
 set(COMPONENT_REQUIRES app_trace)
-
+set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
 register_component()
 
 target_link_libraries(freertos "-Wl,--undefined=uxTopUsedPriority")
index 375ee5ae81d34e944da501ffe6fb634f8cc299b2..dae672e85e69719f55814e36a492d3f939998372 100644 (file)
@@ -7,3 +7,4 @@ COMPONENT_ADD_INCLUDEDIRS := include
 COMPONENT_PRIV_INCLUDEDIRS := include/freertos
 
 tasks.o event_groups.o timers.o queue.o: CFLAGS += -D_ESP_FREERTOS_INTERNAL
+COMPONENT_ADD_LDFRAGMENTS += linker.lf
diff --git a/components/freertos/linker.lf b/components/freertos/linker.lf
new file mode 100644 (file)
index 0000000..93092b7
--- /dev/null
@@ -0,0 +1,5 @@
+[mapping]
+archive: libfreertos.a
+entries:
+    * (noflash_text)
+
index 073635b7a5787b4ec65ff3c52e29f14e15aa8aec..c2ff4d50a6d83dd43f2e129a74b98536be0cc2d4 100644 (file)
@@ -12,7 +12,7 @@ if(CONFIG_HEAP_TASK_TRACKING)
 endif()
 
 set(COMPONENT_ADD_INCLUDEDIRS "include")
-
+set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
 set(COMPONENT_REQUIRES "")
 
 register_component()
index 77fd7997170186981deb955f05b0f85b37ade4d6..7d8ef920af786970f2169037f29da51e47fe2e30 100644 (file)
@@ -20,3 +20,5 @@ WRAP_ARGUMENT := -Wl,--wrap=
 COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME) $(addprefix $(WRAP_ARGUMENT),$(WRAP_FUNCTIONS))
 
 endif
+
+COMPONENT_ADD_LDFRAGMENTS += linker.lf
diff --git a/components/heap/linker.lf b/components/heap/linker.lf
new file mode 100644 (file)
index 0000000..fc79148
--- /dev/null
@@ -0,0 +1,5 @@
+[mapping]
+archive: libheap.a
+entries:
+    multi_heap (noflash)
+    multi_heap_poisoning (noflash)
\ No newline at end of file
index 0188244a2fdbfe94513684676d279cabc125892c..ec532ad8723141ddd4ea04286c44483f07c04845 100644 (file)
@@ -11,5 +11,8 @@ endif()
 list(APPEND COMPONENT_ADD_INCLUDEDIRS include)
 list(APPEND COMPONENT_SRCS "src/memory_layout_utils.c")
 
+set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
+
 set(COMPONENT_REQUIRES)
+
 register_component()
index 99fae13864bcb62f9b4377f33664c21114e17044..e2d92253272a96b0ad0d0758cb09aa29c66001d2 100644 (file)
@@ -5,3 +5,4 @@ COMPONENT_SRCDIRS := $(SOC_NAME) src/
 COMPONENT_ADD_INCLUDEDIRS := $(SOC_NAME)/include include
 
 -include $(COMPONENT_PATH)/$(SOC_NAME)/component.mk
+COMPONENT_ADD_LDFRAGMENTS += linker.lf
diff --git a/components/soc/linker.lf b/components/soc/linker.lf
new file mode 100644 (file)
index 0000000..f9526f1
--- /dev/null
@@ -0,0 +1,12 @@
+[mapping]
+archive: libsoc.a
+entries: 
+    cpu_util (noflash_text)
+    rtc_clk (noflash)
+    rtc_clk_init (noflash_text)
+    rtc_init (noflash_text)
+    rtc_periph (noflash_text)
+    rtc_pm (noflash_text)
+    rtc_sleep (noflash_text)
+    rtc_time (noflash_text)
+    rtc_wdt (noflash_text)
\ No newline at end of file
index 0445b53347aa32553b53b6b32d4bd04ff72558b6..fb3ff44eab35cbde77d575011d3b91bcb7c43d2b 100644 (file)
@@ -15,4 +15,6 @@ endif()
 set(COMPONENT_ADD_INCLUDEDIRS include)
 set(COMPONENT_REQUIRES)
 
+set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
+
 register_component()
index d511eedb8ed2f10bc4ac46037463a0854362d86e..2ff786e2d5e601718c4cbb919b6dc3e794d94656 100644 (file)
@@ -1,7 +1,8 @@
 COMPONENT_ADD_INCLUDEDIRS := include
 
+COMPONENT_ADD_LDFRAGMENTS += linker.lf
+
 ifdef IS_BOOTLOADER_BUILD
 # Bootloader needs updated SPIUnlock from this file
 COMPONENT_OBJS := spi_flash_rom_patch.o
 endif
-
diff --git a/components/spi_flash/linker.lf b/components/spi_flash/linker.lf
new file mode 100644 (file)
index 0000000..35ad334
--- /dev/null
@@ -0,0 +1,5 @@
+[mapping]
+archive: libspi_flash.a
+entries: 
+    spi_flash_rom_patch (noflash_text)
+
index 465690d822da83b5a2d0b531580bc45602eb3a43..d485366ce49b8365d691926071fc8d36f576620f 100644 (file)
@@ -4,4 +4,6 @@ set(COMPONENT_ADD_INCLUDEDIRS "include")
 
 set(COMPONENT_REQUIRES "")
 
+set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
+
 register_component()
index 308f64f0ea1ef4187292c39ff36c1475a022389b..5d39676e291dd4c92898a86f1dc2885471458ce2 100644 (file)
@@ -2,3 +2,4 @@
 # Component Makefile
 #
 # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+COMPONENT_ADD_LDFRAGMENTS += linker.lf
diff --git a/components/xtensa-debug-module/linker.lf b/components/xtensa-debug-module/linker.lf
new file mode 100644 (file)
index 0000000..44dea62
--- /dev/null
@@ -0,0 +1,4 @@
+[mapping]
+archive: libxtensa-debug-module.a
+entries: 
+    eri (noflash_text)
index 0d8b0a07c0b089fcf5bce95b420d844a2b346ad3..3cd1465e8d119236933a8f3cba4266abbf23676c 100644 (file)
@@ -350,6 +350,7 @@ The following variables can be set inside ``component.mk`` to control the build
   ``*.c``, ``*.S``). Source files are globbed from the listed directories and compiled as part of the component in place of ``COMPONENT_SRCS``, i.e. setting this will cause ``COMPONENT_SRCS`` to be ignored.
   This can be a convenient way of including source files to the components en masse, but is generally not recommended due to caveats attached to CMake globbing (see `File Globbing & Incremental Builds`).
 - ``COMPONENT_SRCEXCLUDE``: Paths to source files to exclude from component. Can be set in conjunction with ``COMPONENT_SRCDIRS`` if there is a directory with a large number of source files to include in the component but one or more source files which should not be. Paths can be specified relative to the component directory or absolute.
+- ``COMPONENT_ADD_LDFRAGMENTS``: Paths to linker fragment files for the linker script generation functionality. See :doc:`Linker Script Generation <linker-script-generation>`.
 
 .. note::
 
@@ -688,6 +689,14 @@ Place this line after the ``project()`` line in your project CMakeLists.txt file
 
 For an example of using this technique, see :example:`protocols/https_request` - the certificate file contents are loaded from the text .pem file at compile time.
 
+Code and Data Placements
+------------------------
+
+ESP-IDF has a feature called linker script generation that enables components to define where its code and data will be placed in memory through 
+linker fragment files. These files are processed by the build system, and is used to augment the linker script used for linking 
+app binary. See :doc:`Linker Script Generation <linker-script-generation>` for a quick start guide as well as a detailed discussion
+of the mechanism.
+
 .. _component-build-full-override:
 
 Fully Overriding The Component Build Process
index e1c0c045ebaa4a0161668ed763d11d5fc13349b4..240ae4bc3157bf0f22c8786f8e281b84e5cb43b5 100644 (file)
@@ -274,6 +274,8 @@ The following variables can be set inside ``component.mk`` to control the build
   settings. Component-specific additions can be made via ``CXXFLAGS
   +=``. It is also possible (although not recommended) to override
   this variable completely for a component.
+- ``COMPONENT_ADD_LDFRAGMENTS``: Paths to linker fragment files for the linker 
+  script generation functionality. See :doc:`Linker Script Generation <linker-script-generation>`.
 
 To apply compilation flags to a single source file, you can add a variable override as a target, ie::
 
@@ -570,6 +572,13 @@ The names are generated from the full name of the file, as given in COMPONENT_EM
 
 For an example of using this technique, see :example:`protocols/https_request` - the certificate file contents are loaded from the text .pem file at compile time.
 
+Code and Data Placements
+------------------------
+
+ESP-IDF has a feature called linker script generation that enables components to define where its code and data will be placed in memory through 
+linker fragment files. These files are processed by the build system, and is used to augment the linker script used for linking 
+app binary. See :doc:`Linker Script Generation <linker-script-generation>` for a quick start guide as well as a detailed discussion
+of the mechanism.
 
 Fully Overriding The Component Makefile
 ---------------------------------------
index cf10446864a8ab62b3406d5b27ba64de0493dcd9..0283f555b1f14da95dc18f21a253882a8054933f 100644 (file)
@@ -30,3 +30,4 @@ API Guides
    ESP-MESH <mesh>
    BluFi <blufi>
    External SPI-connected RAM <external-ram>
+   Linker Script Generation <linker-script-generation>
\ No newline at end of file
diff --git a/docs/en/api-guides/linker-script-generation.rst b/docs/en/api-guides/linker-script-generation.rst
new file mode 100644 (file)
index 0000000..5ec246f
--- /dev/null
@@ -0,0 +1,546 @@
+Linker Script Generation
+========================
+
+Overview
+--------
+
+There are several :ref:`memory regions<memory-layout>` where code and data can be placed. Usually, code and read-only data are placed in flash regions,
+writable data in RAM, etc. A common action is changing where code/data are mapped by default, say placing critical code/rodata in RAM for performance
+reasons or placing code/data/rodata in RTC memory for use in a wake stub or the ULP coprocessor.
+
+IDF provides the ability for defining these placements at the component level using the linker script generation mechanism. The component presents
+how it would like to map the input sections of its object files (or even functions/data) through :ref:`linker fragment files<ldgen-fragment-files>`. During app build, 
+the linker fragment files are collected, parsed and processed; and the :ref:`linker script template<ldgen-script-templates>` is augmented with
+information generated from the fragment files to produce the final linker script. This linker script is then used for the linking
+the final app binary.
+
+Quick Start
+------------
+
+This section presents a guide for quickly placing code/data to RAM and RTC memory; as well as demonstrating how to make these placements 
+dependent on project configuration values. In a true quick start fashion, this section glosses over terms and concepts that will be discussed 
+at a later part of the document. However, whenever it does so, it provides a link to the relevant section on the first mention.
+
+.. _ldgen-add-fragment-file :
+
+Preparation
+^^^^^^^^^^^
+
+Make
+""""
+
+Create a linker fragment file inside the component directory, which is just a text file with a .lf extension. In order for the build system to collect your fragment file,
+add an entry to it from the component, set the variable ``COMPONENT_ADD_LDFRAGMENTS`` to your linker file/s before the ``register_component`` call.
+
+.. code-block:: make
+
+    # file paths relative to component Makefile 
+    COMPONENT_ADD_LDFRAGMENTS += "path/to/linker_fragment_file.lf" "path/to/another_linker_fragment_file.lf"
+
+CMake
+"""""
+
+For CMake set the variable ``COMPONENT_ADD_LDFRAGMENTS`` to your linker file/s before the ``register_component`` call.
+
+.. code-block:: cmake
+
+    # file paths relative to CMakeLists.txt  
+    set(COMPONENT_ADD_LDFRAGMENTS "path/to/linker_fragment_file.lf" "path/to/another_linker_fragment_file.lf")
+
+    register_component()
+
+
+Specifying placements
+^^^^^^^^^^^^^^^^^^^^^
+
+This mechanism allows specifying placement of the following entities:
+
+    - one or multiple object files within the component
+    - one or multiple function/variable using their names
+    - the entire component library
+
+For the following text, suppose we have the following:
+
+    - a component named ``component`` that is archived as library ``libcomponent.a`` during build
+    - three object files archived under the library, ``object1.o``, ``object2.o`` and ``object3.o``
+    - under ``object1.o``, the function ``function1`` is defined; under ``object2.o``, the function ``function2`` is defined
+    - there exists configuration ``PERFORMANCE_MODE`` and ``PERFORMANCE_LEVEL`` in one of the IDF KConfig files, with the set value indicated by entries ``CONFIG_PERFORMANCE_MODE`` and ``CONFIG_PERFORMANCE_LEVEL`` in the project sdkconfig
+
+In the created linker fragment file, we write:
+
+.. code-block:: none
+
+    [mapping]
+    archive: libcomponent.a
+    entries:
+
+This creates an empty :ref:`mapping fragment<ldgen-mapping-fragment>`, which doesn't do anything yet. During linking the :ref:`default placements<ldgen-default-placements>` 
+will still be used for ``libcomponent.a``, unless the ``entries`` key is populated.
+
+.. _ldgen-placing-object-files :
+
+Placing object files
+""""""""""""""""""""
+
+Suppose the entirety of ``object1.o``  is performance-critical, so it is desirable to place it in RAM. On the other hand, all of ``object2.o``
+contains things to be executed coming out of deep sleep, so it needs to be put under RTC memory. We can write:
+
+.. code-block:: none
+
+    [mapping]
+    archive: libcomponent.a
+    entries:
+        object1 (noflash)     # places all code / read-only data under IRAM/ DRAM
+        object2 (rtc)         # places all code/ data and read-only data under RTC fast memory/ RTC slow memory
+
+What happens to ``object3.o``? Since it is not specified, default placements are used for ``object3.o``.
+
+Placing functions/data using their names
+""""""""""""""""""""""""""""""""""""""""
+
+Continuing our example, suppose that among functions defined under ``object1.o``, only ``function1`` is performance-critical; and under ``object2.o``,
+only ``function2`` needs to execute after the chip comes out of deep sleep. This could be accomplished by writing:
+
+.. code-block:: none
+
+    [mapping]
+    archive: libcomponent.a
+    entries:
+        object1:function1 (noflash) 
+        object2:function2 (rtc) 
+
+The default placements are used for the rest of the functions in ``object1.o`` and ``object2.o`` and the entire ``object3.o``. Something similar
+can be achieved for placing data by writing the variable name instead of the function name after ``:``.
+
+.. warning::
+
+    There are :ref:`limitations<ldgen-type3-limitations>` in placing code/data using their symbol names. In order to ensure proper placements, an alternative would be to group
+    relevant code and data into source files, and :ref:`use object file placement<ldgen-placing-object-files>`.
+
+Placing entire component
+""""""""""""""""""""""""
+
+In this example, suppose that the entire component needs to be placed in RAM. This can be written as:
+
+.. code-block:: none
+
+    [mapping]
+    archive: libcomponent.a
+    entries:
+        * (noflash)
+
+Similarly, this places the entire component in RTC memory:
+
+.. code-block:: none
+
+    [mapping]
+    archive: libcomponent.a
+    entries:
+        * (rtc)
+
+Configuration-dependent placements
+""""""""""""""""""""""""""""""""""
+
+Suppose that the entire component library should only be placed when ``CONFIG_PERFORMANCE_MODE == y`` in the sdkconfig. This could be written as:
+
+.. code-block:: none
+
+    [mapping]
+    archive: libcomponent.a
+    entries:
+        : PERFORMANCE_MODE = y
+        * (noflash)
+
+In pseudocode, this translates to:
+
+.. code-block:: none
+
+    if PERFORMANCE_MODE = y
+        place entire libcomponent.a in RAM
+    else
+        use default placements
+
+It is also possible to have multiple conditions to test. Suppose the following requirements: when ``CONFIG_PERFORMANCE_LEVEL == 1``, only ``object1.o`` is put in RAM;
+when ``CONFIG_PERFORMANCE_LEVEL == 2``, ``object1.o`` and ``object2.o``; and when ``CONFIG_PERFORMANCE_LEVEL == 3`` all object files under the archive
+are to be put into RAM. When these three are false however, put entire library in RTC memory. This scenario is a bit contrived, but, 
+it can be written as:
+
+.. code-block:: none
+
+    [mapping]
+    archive: libcomponent.a
+    entries:
+        : PERFORMANCE_LEVEL = 3
+        * (noflash)
+        : PERFORMANCE_LEVEL = 2
+        object1 (noflash)
+        object2 (noflash)
+        : PERFORMANCE_LEVEL = 1
+        object1 (noflash)
+        : default
+        * (rtc)
+
+Which reads:
+
+.. code-block:: none
+
+    if CONFIG_PERFORMANCE_LEVEL == 3
+        place entire libcomponent.a in RAM
+    else if CONFIG_PERFORMANCE_LEVEL == 2
+        only place object1.o and object2.o in RAM
+    else if CONFIG_PERFORMANCE_LEVEL == 1
+        only place object1.o in RAM
+    else
+        place entire libcomponent.a in RTC memory 
+
+The conditions test :ref:`support other operations<ldgen-condition-entries>`.
+
+.. _ldgen-default-placements:
+
+The 'default' placements
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Up until this point, the term  'default placements' has been mentioned as fallback placements for when the 
+placement rules ``rtc`` and ``noflash`` are not specified. The tokens ``noflash`` or ``rtc`` are not merely keywords known by the mechanism, but are actually 
+objects called :ref:`scheme fragments<ldgen-scheme-fragment>` that are specified by the user. Due to the commonness of these placement use cases,
+they are pre-defined in IDF.
+
+Similarly, there exists a ``default`` scheme fragment which defines what the default placement rules should be, which is discussed :ref:`here<ldgen-default-scheme>`.
+
+.. note::
+    For an example of an IDF component using this feature, see :component_file:`freertos/CMakeLists.txt`. The ``freertos`` component uses this 
+    mechanism to place all code, literal and rodata of all of its object files to the instruction RAM memory region for performance reasons.
+
+This marks the end of the quick start guide. The following text discusses this mechanism in a little bit more detail, such its components, essential concepts,
+the syntax, how it is integrated with the build system, etc. The following sections should be helpful in creating custom mappings or modifying default 
+behavior.
+
+Components
+----------
+
+.. _ldgen-fragment-files :
+
+Linker Fragment Files
+^^^^^^^^^^^^^^^^^^^^^
+
+The fragment files contain objects called 'fragments'. These fragments contain pieces of information which, when put together, form
+placement rules that tell where to place sections of object files in the output binary.
+
+Another way of putting it is that processing linker fragment files aims to create the section placement rules inside GNU LD ``SECTIONS`` command. 
+Where to collect and put these section placement rules is represented internally as a ``target`` token.
+
+The three types of fragments are discussed below.
+
+.. note::
+
+    Fragments have a name property (except mapping fragments) and are known globally. 
+    Fragment naming follows C variable naming rules, i.e. case sensitive, must begin with a letter or underscore, alphanumeric/underscore after
+    initial characters are allowed, no spaces/special characters. Each type of fragment has its own namespace. In cases where multiple fragments
+    of the same type and name are encountered, an exception is thrown.
+
+.. _ldgen-sections-fragment :
+
+I. Sections
+"""""""""""
+
+Sections fragments defines a list of object file sections that the GCC compiler emits. It may be a default section (e.g. ``.text``, ``.data``) or 
+it may be user defined section through the ``__attribute__`` keyword. 
+
+The use of an optional '+' indicates the inclusion of the section in the list, as well as sections that start with it. This is the preferred method over listing both explicitly. 
+
+**Syntax**
+
+.. code-block:: none
+
+    [sections:name]
+    entries:
+        .section+
+        .section
+        ...
+
+**Example**
+
+.. code-block:: none
+
+    # Non-preferred
+    [sections:text]
+    entries:
+        .text
+        .text.*
+        .literal
+        .literal.*
+
+    # Preferred, equivalent to the one above
+    [sections:text]
+    entries:
+        .text+              # means .text and .text.*
+        .literal+           # means .literal and .literal.*
+
+.. _ldgen-scheme-fragment :
+
+II. Scheme
+""""""""""
+
+Scheme fragments define what ``target`` a sections fragment is assigned to. 
+
+**Syntax**
+
+.. code-block:: none
+
+    [scheme:name]
+    entries:
+        sections -> target
+        sections -> target
+        ...
+
+**Example**
+
+.. code-block:: none
+
+    [scheme:noflash]
+    entries:
+        text -> iram0_text          # the entries under the sections fragment named text will go to iram0_text
+        rodata -> dram0_data        # the entries under the sections fragment named rodata will go to dram0_data
+
+.. _ldgen-default-scheme:
+
+**The** ``default`` **scheme**
+
+There exists a special scheme with the name ``default``. This scheme is special because catch-all placement rules are generated from
+its entries. This means that, if one of its entries is ``text -> flash_text``, the placement rule 
+
+.. code-block:: none
+
+    *(.literal .literal.* .text .text.*)
+
+will be generated for the target ``flash_text``. 
+
+These catch-all rules then effectively serve as fallback rules for those whose mappings were not specified. 
+
+.. note::
+
+    The ``default scheme`` is defined in :component:`esp32/ld/esp32_fragments.lf`. The ``noflash`` and ``rtc`` scheme fragments which are 
+    built-in schemes referenced in the quick start guide are also defined in this file.
+
+.. _ldgen-mapping-fragment :
+
+III. Mapping
+""""""""""""
+
+Mapping fragments define what scheme fragment to use for mappable entities, i.e. object files, function names, variable names. There are two types of entries
+for this fragment: mapping entries and condition entries.
+
+.. note::
+
+    Mapping fragments have no explicit name property. Internally, the name is constructed from the value of the archive entry. 
+
+**Syntax**
+
+.. code-block:: none
+
+    [mapping]
+    archive: archive                # output archive file name, as built (i.e. libxxx.a)
+    entries:
+        : condition                 # condition entry, non-default
+        object:symbol (scheme)      # mapping entry, Type I
+        object (scheme)             # mapping entry, Type II
+        * (scheme)                  # mapping entry, Type III
+
+        # optional separation/comments, for readability
+
+        : default                   # condition entry, default
+        * (scheme)                  # mapping entry, Type III
+
+.. _ldgen-mapping-entries :
+
+**Mapping Entries**
+
+There are three types of mapping entries:
+
+    ``Type I``
+        The object file name and symbol name are specified. The symbol name can be a function name or a variable name.
+
+    ``Type II``
+        Only the object file name is specified.
+
+    ``Type III``
+        ``*`` is specified, which is a short-hand for all the object files under the archive.
+
+To know what a mapping entry means, let us expand a ``Type II`` entry. Originally: 
+
+.. code-block:: none
+
+    object (scheme)
+
+Then expanding the scheme fragment from its entries definitions, we have:
+
+.. code-block:: none
+
+    object (sections -> target, 
+            sections -> target, 
+            ...)
+
+Expanding the sections fragment with its entries definition:
+
+.. code-block:: none
+
+    object (.section,      # given this object file
+            .section,      # put its sections listed here at this
+            ... -> target, # target
+            
+            .section,
+            .section,      # same should be done for these sections
+            ... -> target, 
+            
+            ...)           # and so on
+
+.. _ldgen-type3-limitations :
+
+**On** ``Type I`` **Mapping Entries**
+
+``Type I`` mapping entry is possible due to compiler flags ``-ffunction-sections`` and ``-ffdata-sections``. If the user opts to remove these flags, then
+the ``Type I`` mapping will not work. Furthermore, even if the user does not opt to compile without these flags, there are still limitations 
+as the implementation is dependent on the emitted output sections.
+
+For example, with ``-ffunction-sections``, separate sections are emitted for each function; with section names predictably constructed i.e. ``.text.{func_name}``
+and ``.literal.{func_name}``. This is not the case for string literals within the function, as they go to pooled or generated section names.
+
+With ``-fdata-sections``, for global scope data the compiler predictably emits either ``.data.{var_name}``, ``.rodata.{var_name}`` or ``.bss.{var_name}``; and so ``Type I`` mapping entry works for these. 
+However, this is not the case for static data declared in function scope, as the generated section name is a result of mangling the variable name with some other information.
+
+.. _ldgen-condition-entries :
+
+**Condition Entries**
+
+Condition entries enable the linker script generation to be configuration-aware. Depending on whether expressions involving configuration values
+are true or not, a particular set of mapping entries can be used. The evaluation uses ``eval_string`` from ``:idf_file:`tools/kconfig_new/kconfiglib.py``` and adheres to its required syntax and limitations.
+
+All mapping entries defined after a condition entry until the next one or the end of the mapping fragment belongs to that condition entry. During processing 
+conditions are tested sequentially, and the mapping entries under the first condition that evaluates to ``TRUE`` are used.
+
+A default condition can be defined (though every mapping contains an implicit, empty one), whose mapping entries get used in the event no conditions evaluates to ``TRUE``.
+
+**Example**
+
+.. code-block:: none
+
+    [scheme:noflash]
+    entries:
+        text -> iram0_text
+        rodata -> dram0_data
+
+    [mapping:lwip]
+    archive: liblwip.a
+    entries:
+        : LWIP_IRAM_OPTIMIZATION = y         # if CONFIG_LWIP_IRAM_OPTIMIZATION is set to 'y' in sdkconfig
+        ip4:ip4_route_src_hook (noflash)     # map ip4.o:ip4_route_src_hook, ip4.o:ip4_route_src and
+        ip4:ip4_route_src (noflash)          # ip4.o:ip4_route using the noflash scheme, which puts 
+        ip4:ip4_route (noflash)              # them in RAM
+        
+        : default                            # else no special mapping rules apply
+
+.. _ldgen-script-templates :
+
+Linker Script Template
+^^^^^^^^^^^^^^^^^^^^^^
+
+The linker script template is the skeleton in which the generated placement rules are put into. It is an otherwise ordinary linker script, with a specific marker syntax
+that indicates where the generated placement rules are placed.
+
+**Syntax**
+
+To reference the placement rules collected under a ``target`` token, the following syntax is used:
+
+.. code-block:: none
+
+    mapping[target]
+
+**Example**
+
+The example below is an excerpt from a possible linker script template. It defines an output section ``.iram0.text``, and inside is a marker referencing
+the target ``iram0_text``.
+
+.. code-block:: none
+
+    .iram0.text :
+    {
+        /* Code marked as runnning out of IRAM */
+        _iram_text_start = ABSOLUTE(.);
+
+        /* Marker referencing iram0_text */
+        mapping[iram0_text]
+
+        INCLUDE esp32.spiram.rom-functions-iram.ld
+        _iram_text_end = ABSOLUTE(.);
+    } > iram0_0_seg
+
+Suppose the generator collected the fragment definitions below:
+
+.. code-block:: none
+
+    [sections:text]
+        .text+
+        .literal+
+
+    [sections:iram]
+        .iram1+
+
+    [scheme:default]
+    entries:
+        text -> flash_text
+        iram -> iram0_text
+
+    [scheme:noflash]
+    entries:
+        text -> iram0_text
+
+    [mapping:freertos]
+    archive: libfreertos.a
+    entries:
+        * (noflash)
+
+Then the corresponding excerpt from the generated linker script will be as follows:
+
+.. code-block:: c
+
+    .iram0.text :
+    {
+        /* Code marked as runnning out of IRAM */
+        _iram_text_start = ABSOLUTE(.);
+
+        /* Placement rules generated from the processed fragments, placed where the marker was in the template */
+        *(.iram1 .iram1.*)
+        *libfreertos.a:(.literal .text .literal.* .text.*)
+
+        INCLUDE esp32.spiram.rom-functions-iram.ld
+        _iram_text_end = ABSOLUTE(.);
+    } > iram0_0_seg
+
+``*libfreertos.a:(.literal .text .literal.* .text.*)``
+
+    Rule generated from the entry ``* (noflash)`` of the ``freertos`` mapping fragment. All ``text`` sections of all
+    object files under the archive ``libfreertos.a`` will be collected under the target ``iram0_text`` (as per the ``noflash`` scheme)
+    and placed wherever in the template ``iram0_text`` is referenced by a marker.
+
+``*(.iram1 .iram1.*)``
+
+    Rule generated from the default scheme entry       ``iram -> iram0_text``. Since the default scheme specifies an ``iram -> iram0_text`` entry,
+    it too is placed wherever ``iram0_text`` is referenced by a marker. Since it is a rule generated from the default scheme, it comes first
+    among all other rules collected under the same target name.
+
+
+Integration with Build System
+-----------------------------
+
+The linker script generation occurs during application build, before the final output binary is linked. The tool that implements the mechanism 
+lives under ``$(IDF_PATH)/tools/ldgen``.
+
+Linker Script Template
+^^^^^^^^^^^^^^^^^^^^^^
+Currently, the linker script template used is :component:`esp32/ld/esp32.common.ld.in`, and is used only for the app build. The generated output script is 
+put under the build directory of the same component. Modifying this linker script template triggers a re-link of the app binary. 
+
+Linker Fragment File
+^^^^^^^^^^^^^^^^^^^^
+Any component can add a fragment file to the build. In order to add a fragment file to process, use the command ``ldgen_add_fragment_file`` as mentioned :ref:`here<ldgen-add-fragment-file>`.
+Modifying any fragment file presented to the build system triggers a re-link of the app binary.
index bd4d6ee5c57fa16a31d7498fcb76d17c21a98639..65097212890554bbd0d324834bc03d42a7f7ee78 100644 (file)
@@ -30,3 +30,4 @@ API 指南
    ESP-MESH <mesh>
    BluFi <blufi>
    External SPI-connected RAM <external-ram>
+   Linker Script Generation <linker-script-generation>
diff --git a/docs/zh_CN/api-guides/linker-script-generation.rst b/docs/zh_CN/api-guides/linker-script-generation.rst
new file mode 100644 (file)
index 0000000..0080289
--- /dev/null
@@ -0,0 +1 @@
+.. include:: ../../en/api-guides/linker-script-generation.rst
index a5450747283d72e0a2931ea0e318618f0b6c40e7..449ad9fc735de1562cad0ceb71c4bea2c8b714c3 100644 (file)
@@ -46,6 +46,10 @@ COMPONENT_EMBED_TXTFILES ?=
 COMPONENT_ADD_INCLUDEDIRS = include
 COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME)
 
+# Name of the linker fragment files this component presents to the Linker
+# script generator
+COMPONENT_ADD_LDFRAGMENTS ?=
+
 # Define optional compiling macros
 define compile_exclude
 COMPONENT_OBJEXCLUDE += $(1)
@@ -151,6 +155,8 @@ OWN_INCLUDES:=$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_PRIV_INCLUDED
 COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_INCLUDES))
 
 
+include $(IDF_PATH)/make/ldgen.mk
+
 ################################################################################
 # 4) Define a target to generate component_project_vars.mk Makefile which
 # contains common per-component settings which are included directly in the
@@ -193,8 +199,8 @@ component_project_vars.mk::
        @echo 'COMPONENT_LINKER_DEPS += $(call MakeVariablePath,$(call resolvepath,$(COMPONENT_ADD_LINKER_DEPS),$(COMPONENT_PATH)))' >> $@
        @echo 'COMPONENT_SUBMODULES += $(call MakeVariablePath,$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_SUBMODULES))))' >> $@
        @echo 'COMPONENT_LIBRARIES += $(COMPONENT_NAME)' >> $@
+       @echo 'COMPONENT_LDFRAGMENTS += $(call MakeVariablePath,$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_LDFRAGMENTS))))' >> $@
        @echo 'component-$(COMPONENT_NAME)-build: $(addprefix component-,$(addsuffix -build,$(COMPONENT_DEPENDS)))' >> $@
-
 ################################################################################
 # 5) Where COMPONENT_OWNBUILDTARGET / COMPONENT_OWNCLEANTARGET
 # is not set by component.mk, define default build, clean, etc. targets
@@ -212,7 +218,7 @@ build: $(COMPONENT_LIBRARY)
 $(COMPONENT_LIBRARY): $(COMPONENT_OBJS) $(COMPONENT_EMBED_OBJS)
        $(summary) AR $(patsubst $(PWD)/%,%,$(CURDIR))/$@
        rm -f $@
-       $(AR) $(ARFLAGS) $@ $^
+       $(AR) $(ARFLAGS) $@ $(COMPONENT_OBJS) $(COMPONENT_EMBED_OBJS)
 endif
 
 # If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target
@@ -332,7 +338,7 @@ clean:
        $(summary) RM component_project_vars.mk
        rm -f component_project_vars.mk
 
-component_project_vars.mk::  # no need to add variables via component.mk
+component_project_vars.mk:: # no need to add variables via component.mk
        @echo '# COMPONENT_CONFIG_ONLY target sets no variables here' > $@
 
 endif  # COMPONENT_CONFIG_ONLY
diff --git a/make/ldgen.mk b/make/ldgen.mk
new file mode 100644 (file)
index 0000000..8e9e8e7
--- /dev/null
@@ -0,0 +1,37 @@
+# Makefile to support the linker script generation mechanism
+
+LDGEN_SECTIONS_INFO_FILES = $(foreach lib, $(COMPONENT_LIBRARIES), $(BUILD_DIR_BASE)/$(lib)/lib$(lib).a.sections_info)
+LDGEN_FRAGMENT_FILES = $(COMPONENT_LDFRAGMENTS)
+
+# Target to generate linker script generator from fragments presented by each of
+# the components
+define ldgen_process_template
+$(2): $(1) $(LDGEN_FRAGMENT_FILES) $(SDKCONFIG) $(LDGEN_SECTIONS_INFO_FILES)
+       @echo 'Generating $(notdir $(2))'
+       $(PYTHON) $(IDF_PATH)/tools/ldgen/ldgen.py \
+               --input         $(1) \
+               --config        $(SDKCONFIG) \
+               --fragments     $(LDGEN_FRAGMENT_FILES) \
+               --output        $(2) \
+               --sections      $(LDGEN_SECTIONS_INFO_FILES) \
+               --kconfig       $(IDF_PATH)/Kconfig \
+               --env           "COMPONENT_KCONFIGS=$(COMPONENT_KCONFIGS)" \
+               --env           "COMPONENT_KCONFIGS_PROJBUILD=$(COMPONENT_KCONFIGS_PROJBUILD)" \
+               --env           "IDF_CMAKE=n"
+               --env           "IDF_TARGET=$(IDF_TARGET)"
+endef
+
+define ldgen_create_commands
+$(foreach lib, $(COMPONENT_LIBRARIES), \
+       $(eval $(call ldgen_generate_target_sections_info, $(BUILD_DIR_BASE)/$(lib)/lib$(lib).a)))
+
+ldgen-clean:
+       rm -f $(LDGEN_SECTIONS_INFO_FILES)
+endef
+
+# Target to generate sections info file from objdump of component archive
+define ldgen_generate_target_sections_info
+$(1).sections_info: $(1)
+       @echo 'Generating $(notdir $(1).sections_info)' 
+       $(OBJDUMP) -h $(1) > $(1).sections_info
+endef
index be7521b589713dd7dc9accd5173ceb8c55b68d4c..cf5548c0ce1c136f5a375898f80e2ccf9e88da2a 100644 (file)
@@ -204,6 +204,7 @@ COMPONENT_INCLUDES :=
 COMPONENT_LDFLAGS :=
 COMPONENT_SUBMODULES :=
 COMPONENT_LIBRARIES :=
+COMPONENT_LDFRAGMENTS :=
 
 # COMPONENT_PROJECT_VARS is the list of component_project_vars.mk generated makefiles
 # for each component.
@@ -381,6 +382,7 @@ CC ?= gcc
 LD ?= ld
 AR ?= ar
 OBJCOPY ?= objcopy
+OBJDUMP ?= objdump
 SIZE ?= size
 
 # Set host compiler and binutils
@@ -398,8 +400,9 @@ CXX := $(call dequote,$(CONFIG_TOOLPREFIX))c++
 LD := $(call dequote,$(CONFIG_TOOLPREFIX))ld
 AR := $(call dequote,$(CONFIG_TOOLPREFIX))ar
 OBJCOPY := $(call dequote,$(CONFIG_TOOLPREFIX))objcopy
+OBJDUMP := $(call dequote,$(CONFIG_TOOLPREFIX))objdump
 SIZE := $(call dequote,$(CONFIG_TOOLPREFIX))size
-export CC CXX LD AR OBJCOPY SIZE
+export CC CXX LD AR OBJCOPY OBJDUMP SIZE
 
 COMPILER_VERSION_STR := $(shell $(CC) -dumpversion)
 COMPILER_VERSION_NUM := $(subst .,,$(COMPILER_VERSION_STR))
@@ -416,6 +419,18 @@ APP_ELF:=$(BUILD_DIR_BASE)/$(PROJECT_NAME).elf
 APP_MAP:=$(APP_ELF:.elf=.map)
 APP_BIN:=$(APP_ELF:.elf=.bin)
 
+# once we know component paths, we can include the config generation targets
+#
+# (bootloader build doesn't need this, config is exported from top-level)
+ifndef IS_BOOTLOADER_BUILD
+include $(IDF_PATH)/make/project_config.mk
+endif
+
+# include linker script generation utils makefile
+include $(IDF_PATH)/make/ldgen.mk
+
+$(eval $(call ldgen_create_commands))
+
 # Include any Makefile.projbuild file letting components add
 # configuration at the project level
 define includeProjBuildMakefile
@@ -427,13 +442,6 @@ $(foreach componentpath,$(COMPONENT_PATHS), \
        $(if $(wildcard $(componentpath)/Makefile.projbuild), \
                $(eval $(call includeProjBuildMakefile,$(componentpath)))))
 
-# once we know component paths, we can include the config generation targets
-#
-# (bootloader build doesn't need this, config is exported from top-level)
-ifndef IS_BOOTLOADER_BUILD
-include $(IDF_PATH)/make/project_config.mk
-endif
-
 # ELF depends on the library archive files for COMPONENT_LIBRARIES
 # the rules to build these are emitted as part of GenerateComponentTarget below
 #
@@ -538,7 +546,7 @@ endif
 # _config-clean), so config remains valid during all component clean
 # targets
 config-clean: app-clean bootloader-clean
-clean: app-clean bootloader-clean config-clean
+clean: app-clean bootloader-clean config-clean ldgen-clean
 
 # phony target to check if any git submodule listed in COMPONENT_SUBMODULES are missing
 # or out of date, and exit if so. Components can add paths to this variable.
index 729edc2cf4b7964dd82df0997cb71558963e45dc..65f765fe2ed0a924366ac85cac82ae5f4a913bed 100644 (file)
@@ -64,3 +64,6 @@ docs/gen-dxd.py
 tools/ci/multirun_with_pyenv.sh
 components/espcoredump/test/test_espcoredump.py
 components/espcoredump/test/test_espcoredump.sh
+tools/ldgen/ldgen.py
+tools/ldgen/test/test_fragments.py
+tools/ldgen/test/test_generation.py
\ No newline at end of file
index efc3703b180b95fee925da3e213dc336b4d5ccba..394aba510c842422148b363d251626cb5606fba3 100755 (executable)
@@ -157,9 +157,16 @@ function run_tests()
     make
     assert_rebuilt ${APP_BINS} ${BOOTLOADER_BINS}
 
-    print_status "Touching app-only ld file should only re-link app"
+    print_status "Touching app-only template ld file should only re-link app"
     take_build_snapshot
-    touch ${IDF_PATH}/components/esp32/ld/esp32.common.ld
+    touch ${IDF_PATH}/components/esp32/ld/esp32.common.ld.in
+    make
+    assert_rebuilt ${APP_BINS}
+    assert_not_rebuilt ${BOOTLOADER_BINS}
+
+    print_status "Touching a linker fragment file should trigger re-link of app" # only app linker script is generated by tool for now
+    take_build_snapshot
+    touch ${IDF_PATH}/components/esp32/linker.lf
     make
     assert_rebuilt ${APP_BINS}
     assert_not_rebuilt ${BOOTLOADER_BINS}
index 60efc2f326cce3b75d390fc2a298f77ce6d7fa79..1e38e189009ae582cd34daaa26079f9d8bc82f75 100755 (executable)
@@ -143,13 +143,23 @@ function run_tests()
 
     print_status "Updating app-only ld file should only re-link app"
     take_build_snapshot
-    cp ${IDF_PATH}/components/esp32/ld/esp32.common.ld .
+    cp ${IDF_PATH}/components/esp32/ld/esp32.common.ld.in .
     sleep 1  # ninja may ignore if the timestamp delta is too low
-    echo "/* (Build test comment) */" >> ${IDF_PATH}/components/esp32/ld/esp32.common.ld
+    echo "/* (Build test comment) */" >> ${IDF_PATH}/components/esp32/ld/esp32.common.ld.in
     idf.py build || failure "Failed to rebuild with modified linker script"
     assert_rebuilt ${APP_BINS}
     assert_not_rebuilt ${BOOTLOADER_BINS}
-    mv esp32.common.ld ${IDF_PATH}/components/esp32/ld/
+    mv esp32.common.ld.in ${IDF_PATH}/components/esp32/ld/
+
+    print_status "Updating fragment file should only re-link app" # only app linker script is generated by tool for now
+    take_build_snapshot
+    cp ${IDF_PATH}/components/esp32/ld/esp32_fragments.lf .
+    sleep 1  # ninja may ignore if the timestamp delta is too low
+    echo "# (Build test comment)" >> ${IDF_PATH}/components/esp32/ld/esp32_fragments.lf
+    idf.py build || failure "Failed to rebuild with modified linker fragment file"
+    assert_rebuilt ${APP_BINS}
+    assert_not_rebuilt ${BOOTLOADER_BINS}
+    mv esp32_fragments.lf ${IDF_PATH}/components/esp32/ld/
 
     print_status "sdkconfig update triggers full recompile"
     clean_build_dir
index 0a3570518e7d7435512796a03960ac69793238b9..8da2c284fa99467e743870a7137677681477e8fd 100644 (file)
@@ -107,6 +107,11 @@ function(register_component)
         target_link_libraries(${component} "-L${CMAKE_CURRENT_BINARY_DIR}")
         target_link_libraries(${component} "-Wl,--whole-archive -l${component} -Wl,--no-whole-archive")
     endif()
+
+    if(COMPONENT_ADD_LDFRAGMENTS)
+        spaces2list(COMPONENT_ADD_LDFRAGMENTS)
+        ldgen_add_fragment_files(${component} "${COMPONENT_ADD_LDFRAGMENTS}")
+    endif()
 endfunction()
 
 function(register_config_only_component)
diff --git a/tools/cmake/ldgen.cmake b/tools/cmake/ldgen.cmake
new file mode 100644 (file)
index 0000000..26ec26f
--- /dev/null
@@ -0,0 +1,72 @@
+# Utilities for supporting linker script generation in the build system
+
+# ldgen_create_target
+#
+# Create the custom target to attach the fragment files and template files
+# for the build to.
+function(ldgen_set_variables)
+    add_custom_target(ldgen_section_infos)
+    add_custom_target(ldgen DEPENDS ldgen_section_infos)
+endfunction()
+
+# ldgen_add_fragment_file
+#
+# Add one or more linker fragment files, and append it to the list of fragment
+# files found so far.
+function(ldgen_add_fragment_files target fragment_files)
+    spaces2list(fragment_files)
+
+    foreach(fragment_file ${fragment_files})
+        get_filename_component(fragment_file_abs_dir ${fragment_file} ABSOLUTE BASE_DIR ${component_dir})
+        list(APPEND fragment_files_full_path ${fragment_file_abs_dir})
+    endforeach()
+
+    set_property(TARGET ldgen APPEND PROPERTY FRAGMENT_FILES ${fragment_files_full_path})
+
+    get_filename_component(target_sections_info ${CMAKE_CURRENT_BINARY_DIR}/${target}.sections_info ABSOLUTE)
+
+    add_custom_command(
+        OUTPUT ${target_sections_info}
+        COMMAND ${CMAKE_OBJDUMP} $<TARGET_FILE:${target}> -h >  ${target_sections_info}
+        DEPENDS ${target}
+        )
+
+    add_custom_target(${target}_sections_info DEPENDS ${target_sections_info})
+    add_dependencies(ldgen_section_infos ${target}_sections_info)
+
+    set_property(TARGET ldgen_section_infos APPEND PROPERTY SECTIONS_INFO_FILES ${target_sections_info})
+endfunction()
+
+# ldgen_process_template
+#
+# Passes a linker script template to the linker script generation tool for
+# processing
+function(ldgen_process_template template output)
+    # Create command to invoke the linker script generator tool.
+    add_custom_command(
+        OUTPUT ${output}
+        COMMAND ${IDF_PATH}/tools/ldgen/ldgen.py
+        --config    ${SDKCONFIG}
+        --fragments "$<JOIN:$<TARGET_PROPERTY:ldgen,FRAGMENT_FILES>,\t>"
+        --input     ${template}
+        --output    ${output}
+        --sections  "$<JOIN:$<TARGET_PROPERTY:ldgen_section_infos,SECTIONS_INFO_FILES>,\t>"
+        --kconfig   ${IDF_PATH}/Kconfig
+        --env       "COMPONENT_KCONFIGS=${COMPONENT_KCONFIGS}"
+        --env       "COMPONENT_KCONFIGS_PROJBUILD=${COMPONENT_KCONFIGS_PROJBUILD}"
+        --env       "IDF_CMAKE=y"
+        --env       "IDF_TARGET=${IDF_TARGET}"
+        DEPENDS     ${template} $<TARGET_PROPERTY:ldgen,FRAGMENT_FILES> ${SDKCONFIG} ldgen_section_infos
+    )
+
+    get_filename_component(output_name ${output} NAME)
+    add_custom_target(ldgen_${output_name}_script DEPENDS ${output})
+    add_dependencies(ldgen ldgen_${output_name}_script)
+endfunction()
+
+# ldgen_create_commands
+#
+# Create the command to generate the output scripts from templates presented.
+function(ldgen_add_dependencies executable_name)
+    add_dependencies(${executable_name} ldgen)
+endfunction()
\ No newline at end of file
index 94ea567bb56726310ba480d9b18ec13cf45a5656..74042fd07fe5d8b495e3e40ffc0978fe5507ecdb 100644 (file)
@@ -27,6 +27,7 @@ include(targets)
 include(kconfig)
 include(git_submodules)
 include(idf_functions)
+include(ldgen)
 
 set_default(PYTHON "python")
 
@@ -131,6 +132,16 @@ macro(project name)
     ## if project uses git, retrieve revision
     git_describe(PROJECT_VER "${CMAKE_CURRENT_SOURCE_DIR}")
 
+    #
+    # Add the app executable to the build (has name of PROJECT.elf)
+    #
+    idf_add_executable()
+
+    #
+    # Setup variables for linker script generation
+    #
+    ldgen_set_variables()
+
     # Include any top-level project_include.cmake files from components
     foreach(component ${BUILD_COMPONENT_PATHS})
         set(COMPONENT_PATH "${component}")
@@ -156,10 +167,9 @@ macro(project name)
     unset(COMPONENT_NAME)
     unset(COMPONENT_PATH)
 
-    #
-    # Add the app executable to the build (has name of PROJECT.elf)
-    #
-    idf_add_executable()
+    # At this point the fragment files have been collected, generate
+    # the commands needed to generate the output linker scripts
+    ldgen_add_dependencies(${PROJECT_NAME}.elf)
 
     # Write project description JSON file
     make_json_list("${BUILD_COMPONENTS}" build_components_json)
index f92f62da12a7920ee248c28d40950a6e70ff2268..55e293bdd24134c833536f010c1da0774f5d3280 100644 (file)
@@ -85,6 +85,9 @@ function(require_idf_targets)
     endif()
 endfunction()
 
+# Dummy call for ldgen_add_fragment_file
+function(ldgen_add_fragment_file files)
+endfunction()
 
 # expand_component_requirements: Recursively expand a component's requirements,
 # setting global properties BUILD_COMPONENTS & BUILD_COMPONENT_PATHS and
diff --git a/tools/ldgen/__init__.py b/tools/ldgen/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tools/ldgen/fragments.py b/tools/ldgen/fragments.py
new file mode 100644 (file)
index 0000000..f088515
--- /dev/null
@@ -0,0 +1,249 @@
+#!/usr/bin/env python
+#
+# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import re
+import collections
+import sys
+import os
+
+from sdkconfig import SDKConfig
+from pyparsing import *
+
+"""
+Fragment file internal representation. Parses and stores instances of the fragment definitions
+contained within the file.
+"""
+class FragmentFileModel():
+
+    def __init__(self, fragment_file):
+        path = os.path.realpath(fragment_file.name)
+
+        sections = Sections.get_fragment_grammar()
+        scheme = Scheme.get_fragment_grammar()
+        mapping = Mapping.get_fragment_grammar()
+
+        # Each fragment file is composed of sections, scheme or mapping fragments. The grammar
+        # for each of those objects are defined it the respective classes
+        parser = OneOrMore(sections | scheme | mapping)
+
+        # Set any text beginnning with # as comment
+        parser.ignore("#" + restOfLine)
+
+        self.fragments = parser.parseFile(fragment_file, parseAll=True)
+
+        for fragment in self.fragments:
+            fragment.path = path
+
+"""
+Encapsulates a fragment as defined in the generator syntax. Sets values common to all fragment and performs processing
+such as checking the validity of the fragment name and getting the entry values.
+"""
+class Fragment:
+
+    IDENTIFIER = Word(alphas+"_", alphanums+"_")
+    ENTITY = Word(alphanums + ".-_$")
+
+    def __init__(self, name, entries):
+        self.path = None
+        self.name = name
+        self.entries = entries
+
+class Sections(Fragment):
+
+    def __init__(self, name, entries):
+        Fragment.__init__(self, name, entries)
+        self._process_entries()
+
+    def _process_entries(self):
+        # Quietly ignore duplicate entries
+        self.entries = set(self.entries)
+        self.entries = list(self.entries)
+
+    """
+    Utility function that returns a list of sections given a sections fragment entry,
+    with the '+' notation and symbol concatenation handled automatically.
+    """
+    @staticmethod
+    def get_section_data_from_entry(sections_entry, symbol=None):
+        if not symbol:
+            sections = list()
+            sections.append(sections_entry.replace("+", ""))
+            sections.append(sections_entry.replace("+", ".*"))
+            return sections
+        else:
+            if sections_entry.endswith("+"):
+                section = sections_entry.replace("+", ".*")
+                expansion = section.replace(".*", "." + symbol)
+                return (section, expansion)
+            else:
+                return (sections_entry, None)
+
+    @staticmethod
+    def get_fragment_grammar():
+        name = Fragment.IDENTIFIER
+        header = Suppress("[") + Suppress("sections") + Suppress(":") + name.setResultsName("name") + Suppress("]")
+        entry = Word(alphanums + "+" + ".")
+        entries = Suppress("entries") + Suppress(":") + Group(OneOrMore(entry)).setResultsName("entries")
+
+        sections = Group(header + entries)
+
+        sections.setParseAction(lambda t: Sections(t[0].name, t[0].entries))
+
+        sections.ignore("#" + restOfLine)
+
+        return sections
+
+"""
+Encapsulates a scheme fragment, which defines what target input sections are placed under.
+"""
+class Scheme(Fragment):
+
+    def __init__(self, name, items):
+        Fragment.__init__(self, name, items)
+        self._process_entries()
+
+    def _process_entries(self):
+        processed = set()
+
+        # Store entries as a set of tuples. Quietly ignores duplicate entries.
+        for entry in self.entries:
+            processed.add((entry.sections, entry.target))
+
+        self.entries = processed
+
+    @staticmethod
+    def get_fragment_grammar():
+        name = Fragment.IDENTIFIER
+        header = Suppress("[") + Suppress("scheme") + Suppress(":") + name.setResultsName("name") + Suppress("]")
+
+        # Scheme entry in the form 'sections -> target'
+        sections = Fragment.IDENTIFIER
+        target = Fragment.IDENTIFIER
+        entry = Group(sections.setResultsName("sections") + Suppress("->") + target.setResultsName("target"))
+
+        entries = Suppress("entries") + Suppress(":") + Group(OneOrMore(entry)).setResultsName("entries")
+
+        scheme = Group(header + entries)
+
+        scheme.setParseAction(lambda t: Scheme(t[0].name, t[0].entries))
+
+        scheme.ignore("#" + restOfLine)
+
+        return scheme
+
+"""
+Encapsulates a mapping fragment, which defines what targets the input sections of mappable entties are placed under.
+"""
+class Mapping(Fragment):
+
+    # Name of the default condition entry
+    DEFAULT_CONDITION = "default"
+    MAPPING_ALL_OBJECTS = "*"
+
+    def __init__(self, archive, entries):
+        self.archive = archive
+
+        # Generate name from archive value by replacing all non-alphanumeric
+        # characters with underscore
+        name = Mapping.get_mapping_name_from_archive(self.archive)
+        Fragment.__init__(self, name, entries)
+
+        self._process_entries()
+
+    def _create_mappings_set(self, mappings):
+        mapping_set = set()
+
+        for mapping in mappings:
+            obj = mapping.object
+            symbol = mapping.symbol
+            scheme = mapping.scheme
+
+            if symbol == "":
+                symbol = None
+
+            # Quietly handle duplicate definitions under the same condition
+            mapping_set.add((obj, symbol, scheme))
+
+        return mapping_set
+
+    def _process_entries(self):
+        processed = []
+
+        for normal_group in self.entries.normal_groups:
+            # Get the original string of the condition
+            condition  = next(iter(normal_group.condition.asList())).strip()
+            mappings = self._create_mappings_set(normal_group.mappings)
+
+            processed.append((condition, mappings))
+
+        default_group = self.entries.default_group
+        mappings = self._create_mappings_set(default_group.mappings)
+        processed.append(("default", mappings))
+
+        self.entries = processed
+
+    @staticmethod
+    def get_mapping_name_from_archive(archive):
+        return re.sub(r"[^0-9a-zA-Z]+", "_", archive)
+
+    @staticmethod
+    def get_fragment_grammar():
+
+        # Match header [mapping]
+        header = Suppress("[") + Suppress("mapping") + Suppress("]")
+
+        # Define possbile values for input archive and object file
+        filename = Word(alphanums + "-" + "_")
+
+        # There are three possible patterns for mapping entries:
+        #       obj:symbol (scheme)
+        #       obj (scheme)
+        #       * (scheme)
+        obj = Fragment.ENTITY.setResultsName("object")
+        symbol = Suppress(":") + Fragment.IDENTIFIER.setResultsName("symbol")
+        scheme = Suppress("(") + Fragment.IDENTIFIER.setResultsName("scheme") + Suppress(")")
+
+        pattern1 = Group(obj + symbol + scheme)
+        pattern2 = Group(obj + scheme)
+        pattern3 = Group(Literal(Mapping.MAPPING_ALL_OBJECTS).setResultsName("object") + scheme)
+
+        mapping_entry = pattern1 | pattern2 | pattern3
+
+        # To simplify parsing, classify groups of condition-mapping entry into two types: normal and default
+        # A normal grouping is one with a non-default condition. The default grouping is one which contains the
+        # default condition
+        mapping_entries = Group(ZeroOrMore(mapping_entry)).setResultsName("mappings")
+
+        normal_condition = Suppress(":") + originalTextFor(SDKConfig.get_expression_grammar())
+        default_condition = Optional(Suppress(":") + Literal(Mapping.DEFAULT_CONDITION))
+
+        normal_group = Group(normal_condition.setResultsName("condition") + mapping_entries)
+        default_group = Group(default_condition + mapping_entries).setResultsName("default_group")
+
+        normal_groups = Group(ZeroOrMore(normal_group)).setResultsName("normal_groups")
+
+        # Any mapping fragment definition can have zero or more normal group and only one default group as a last entry.
+        archive = Suppress("archive") + Suppress(":") + Fragment.ENTITY.setResultsName("archive")
+        entries = Suppress("entries") + Suppress(":") + (normal_groups + default_group).setResultsName("entries")
+
+        mapping = Group(header + archive + entries)
+
+        mapping.setParseAction(lambda t: Mapping(t[0].archive, t[0].entries))
+
+        mapping.ignore("#" + restOfLine)
+
+        return mapping
diff --git a/tools/ldgen/generation.py b/tools/ldgen/generation.py
new file mode 100644 (file)
index 0000000..f3ee5f2
--- /dev/null
@@ -0,0 +1,643 @@
+#!/usr/bin/env python
+#
+# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import re
+import collections
+import itertools
+import os
+import subprocess
+import fnmatch
+
+from sdkconfig import SDKConfig
+from fragments import FragmentFileModel, Sections, Scheme, Mapping, Fragment
+from pyparsing import *
+
+"""
+Encapsulates a generated placement rule placed under a target
+"""
+class PlacementRule():
+
+    DEFAULT_SPECIFICITY = 0
+    ARCHIVE_SPECIFICITY = 1
+    OBJECT_SPECIFICITY = 2
+    SYMBOL_SPECIFICITY = 3
+
+    class __container():
+        def __init__(self, content):
+            self.content = content
+
+    __metadata = collections.namedtuple("__metadata", "excludes expansions expanded")
+
+    def __init__(self, archive, obj, symbol, sections, target):
+        if archive == "*":
+            archive = None
+
+        if obj == "*":
+            obj = None
+
+        self.archive = archive
+        self.obj = obj
+        self.symbol = symbol
+        self.target = target
+        self.sections = dict()
+
+        self.specificity = 0
+        self.specificity += 1 if self.archive else 0
+        self.specificity += 1 if (self.obj and not self.obj == '*') else 0
+        self.specificity += 1 if self.symbol else 0
+
+        for section in sections:
+            section_data = Sections.get_section_data_from_entry(section, self.symbol)
+
+            if not self.symbol:
+                for s in section_data:
+                    metadata = self.__metadata(self.__container([]), self.__container([]), self.__container(False))
+                    self.sections[s] = metadata
+            else:
+                (section, expansion) = section_data
+                if expansion:
+                    metadata = self.__metadata(self.__container([]), self.__container([expansion]), self.__container(True))
+                    self.sections[section] =  metadata
+
+    def get_section_names(self):
+        return self.sections.keys()
+
+    def add_exclusion(self, other, sections_infos = None):
+        # Utility functions for this method
+        def do_section_expansion(rule, section):
+            if section in rule.get_section_names():
+                sections_in_obj = sections_infos.get_obj_sections(rule.archive, rule.obj)
+
+                expansions = fnmatch.filter(sections_in_obj, section)
+                return expansions
+
+        def remove_section_expansions(rule, section, expansions):
+            existing_expansions = self.sections[section].expansions.content
+            self.sections[section].expansions.content = [e for e in existing_expansions if e not in expansions]
+
+        # Exit immediately if the exclusion to be added is more general than this rule.
+        if not other.is_more_specific_rule_of(self):
+            return
+
+        for section in self.get_sections_intersection(other):
+            if(other.specificity == PlacementRule.SYMBOL_SPECIFICITY):
+                # If this sections has not been expanded previously, expand now and keep track.
+                previously_expanded = self.sections[section].expanded.content
+                if not previously_expanded:
+                    expansions = do_section_expansion(self, section)
+                    if expansions:
+                        self.sections[section].expansions.content = expansions
+                        self.sections[section].expanded.content = True
+                        previously_expanded = True
+
+                # Remove the sections corresponding to the symbol name
+                remove_section_expansions(self, section, other.sections[section].expansions.content)
+
+                # If it has been expanded previously but now the expansions list is empty,
+                # it means adding exclusions has exhausted the list. Remove the section entirely.
+                if previously_expanded and not self.sections[section].expanded.content:
+                    del self.sections[section]
+            else:
+                # A rule section can have multiple rule sections excluded from it. Get the
+                # most specific rule from the list, and if an even more specific rule is found,
+                # replace it entirely. Otherwise, keep appending.
+                exclusions = self.sections[section].excludes
+                exclusions_list = exclusions.content if exclusions.content != None else []
+                exclusions_to_remove = filter(lambda r: r.is_more_specific_rule_of(other), exclusions_list)
+
+                remaining_exclusions = [e for e in exclusions_list if e not in exclusions_to_remove]
+                remaining_exclusions.append(other)
+
+                self.sections[section].excludes.content = remaining_exclusions
+
+    def get_sections_intersection(self, other):
+        return set(self.sections.keys()).intersection(set(other.sections.keys()))
+
+    def is_more_specific_rule_of(self, other):
+        if (self.specificity <= other.specificity):
+            return False
+
+        # Compare archive, obj and target
+        for entity_index in range (1, other.specificity + 1):
+            if self[entity_index] != other[entity_index] and other[entity_index] != None:
+                return False
+
+        return True
+
+    def maps_same_entities_as(self, other):
+        if self.specificity != other.specificity:
+            return False
+
+        # Compare archive, obj and target
+        for entity_index in range (1, other.specificity + 1):
+            if self[entity_index] != other[entity_index] and other[entity_index] != None:
+                return False
+
+        return True
+
+    def __getitem__(self, key):
+        if key == PlacementRule.ARCHIVE_SPECIFICITY:
+            return  self.archive
+        elif key == PlacementRule.OBJECT_SPECIFICITY:
+            return self.obj
+        elif key == PlacementRule.SYMBOL_SPECIFICITY:
+            return self.symbol
+        else:
+            return None
+
+    def __str__(self):
+        sorted_sections = sorted(self.get_section_names())
+
+        sections_string = list()
+
+        for section in sorted_sections:
+            exclusions = self.sections[section].excludes.content
+
+            exclusion_string = None
+
+            if exclusions:
+                exclusion_string = " ".join(map(lambda e: "*" + e.archive + (":" + e.obj + ".*" if e.obj else ""), exclusions))
+                exclusion_string = "EXCLUDE_FILE(" + exclusion_string + ")"
+            else:
+                exclusion_string = ""
+
+            section_string = None
+            exclusion_section_string = None
+
+            section_expansions = self.sections[section].expansions.content
+            section_expanded = self.sections[section].expanded.content
+
+            if section_expansions and section_expanded:
+                section_string = " ".join(section_expansions)
+                exclusion_section_string = section_string
+            else:
+                section_string = section
+                exclusion_section_string = exclusion_string + " " + section_string
+
+            sections_string.append(exclusion_section_string)
+
+        sections_string = " ".join(sections_string)
+
+        archive = str(self.archive) if self.archive else ""
+        obj = (str(self.obj) +  (".*" if self.obj else "")) if self.obj else ""
+
+        # Handle output string generation based on information available
+        if self.specificity == PlacementRule.DEFAULT_SPECIFICITY:
+            rule_string = "*(%s)" % (sections_string)
+        elif self.specificity == PlacementRule.ARCHIVE_SPECIFICITY:
+            rule_string = "*%s:(%s)" % (archive, sections_string)
+        else:
+            rule_string = "*%s:%s(%s)" % (archive, obj, sections_string)
+
+        return rule_string
+
+    def __eq__(self, other):
+        if id(self) == id(other):
+            return True
+
+        def exclusions_set(exclusions):
+            exclusions_set = {(e.archive, e.obj, e.symbol, e.target) for e in exclusions}
+            return exclusions_set
+
+        if self.archive != other.archive:
+            return False
+
+        if self.obj != other.obj:
+            return False
+
+        if self.symbol != other.symbol:
+            return False
+
+        if set(self.sections.keys()) != set(other.sections.keys()):
+            return False
+
+        for (section, metadata) in self.sections.items():
+
+            self_meta = metadata
+            other_meta = other.sections[section]
+
+            if exclusions_set(self_meta.excludes.content) != exclusions_set(other_meta.excludes.content):
+                return False
+
+            if set(self_meta.expansions.content) != set(other_meta.expansions.content):
+                return False
+
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __iter__(self):
+        yield self.archive
+        yield self.obj
+        yield self.symbol
+        raise StopIteration
+
+"""
+Implements generation of placement rules based on collected sections, scheme and mapping fragment.
+"""
+class GenerationModel:
+
+    DEFAULT_SCHEME = "default"
+
+    def __init__(self):
+        self.schemes = {}
+        self.sections = {}
+        self.mappings = {}
+
+    def _add_mapping_rules(self, archive, obj, symbol, scheme_name, scheme_dict, rules):
+        # Use an ordinary dictionary to raise exception on non-existing keys
+        temp_dict = dict(scheme_dict)
+
+        sections_bucket = temp_dict[scheme_name]
+
+        for (target, sections) in sections_bucket.items():
+            section_entries = []
+
+            for section in sections:
+                section_entries.extend(section.entries)
+
+            rule = PlacementRule(archive, obj, symbol, section_entries, target)
+
+            if not rule in rules:
+                rules.append(rule)
+
+    def _build_scheme_dictionary(self):
+        scheme_dictionary = collections.defaultdict(dict)
+
+        # Collect sections into buckets based on target name
+        for scheme in self.schemes.values():
+            sections_bucket = collections.defaultdict(list)
+
+            for (sections_name, target_name) in scheme.entries:
+                # Get the sections under the bucket 'target_name'. If this bucket does not exist
+                # is is created automatically
+                sections_in_bucket = sections_bucket[target_name]
+
+                try:
+                    sections = self.sections[sections_name]
+                except KeyError:
+                    message = GenerationException.UNDEFINED_REFERENCE + " to sections '" + sections + "'."
+                    raise GenerationException(message, scheme)
+
+                sections_in_bucket.append(sections)
+
+            scheme_dictionary[scheme.name] = sections_bucket
+
+        # Search for and raise exception on first instance of sections mapped to multiple targets
+        for (scheme_name, sections_bucket) in scheme_dictionary.items():
+            for sections_a, sections_b in itertools.combinations(sections_bucket.values(), 2):
+                set_a = set()
+                set_b = set()
+
+                for sections in sections_a:
+                    set_a.update(sections.entries)
+
+                for sections in sections_b:
+                    set_b.update(sections.entries)
+
+                intersection = set_a.intersection(set_b)
+
+                # If the intersection is a non-empty set, it means sections are mapped to multiple
+                # targets. Raise exception.
+                if intersection:
+                    scheme = self.schemes[scheme_name]
+                    message = "Sections " + str(intersection) + " mapped to multiple targets."
+                    raise GenerationException(message, scheme)
+
+        return scheme_dictionary
+
+    def generate_rules(self, sdkconfig, sections_infos):
+        placement_rules = collections.defaultdict(list)
+
+        scheme_dictionary = self._build_scheme_dictionary()
+
+        # Generate default rules
+        default_rules = list()
+        self._add_mapping_rules(None, None, None, GenerationModel.DEFAULT_SCHEME, scheme_dictionary, default_rules)
+
+        all_mapping_rules = collections.defaultdict(list)
+
+        # Generate rules based on mapping fragments
+        for mapping in self.mappings.values():
+            for (condition, entries) in mapping.entries:
+                condition_true = False
+
+                # Only non-default condition are evaluated agains sdkconfig model
+                if condition != Mapping.DEFAULT_CONDITION:
+                    try:
+                        condition_true = sdkconfig.evaluate_expression(condition)
+                    except Exception as e:
+                        raise GenerationException(e.message, mapping)
+                else:
+                    condition_true = True
+
+                if condition_true:
+
+                    mapping_rules = list()
+
+                    archive = mapping.archive
+                    for (obj, symbol, scheme_name) in entries:
+                        try:
+                            self._add_mapping_rules(archive, obj, symbol, scheme_name, scheme_dictionary, mapping_rules)
+                        except KeyError:
+                            message = GenerationException.UNDEFINED_REFERENCE + " to scheme '" + scheme_name + "'."
+                            raise GenerationException(message, mapping)
+
+                    all_mapping_rules[mapping.name] = mapping_rules
+
+                    break   # Exit on first condition that evaluates to true
+
+        # Detect rule conflicts
+        for mapping_rules in all_mapping_rules.items():
+            self._detect_conflicts(mapping_rules)
+
+        # Add exclusions
+        for mapping_rules in all_mapping_rules.values():
+            self._create_exclusions(mapping_rules, default_rules, sections_infos)
+
+        # Add the default rules grouped by target
+        for default_rule in default_rules:
+            existing_rules = placement_rules[default_rule.target]
+            if default_rule.get_section_names():
+                existing_rules.append(default_rule)
+
+        for mapping_rules in all_mapping_rules.values():
+            # Add the mapping rules grouped by target
+            for mapping_rule in mapping_rules:
+                existing_rules = placement_rules[mapping_rule.target]
+                if mapping_rule.get_section_names():
+                    existing_rules.append(mapping_rule)
+
+        return placement_rules
+
+    def _detect_conflicts(self, rules):
+        (archive, rules_list) = rules
+
+        for specificity in range(0, PlacementRule.OBJECT_SPECIFICITY + 1):
+            rules_with_specificity = filter(lambda r: r.specificity == specificity, rules_list)
+
+            for rule_a, rule_b in itertools.combinations(rules_with_specificity, 2):
+                intersections = rule_a.get_sections_intersection(rule_b)
+
+                if intersections and rule_a.maps_same_entities_as(rule_b):
+                    rules_string = str([str(rule_a), str(rule_b)])
+                    message = "Rules " + rules_string + " map sections " + str(list(intersections)) + " into multiple targets."
+                    mapping = self.mappings[Mapping.get_mapping_name_from_archive(archive)]
+                    raise GenerationException(message, mapping)
+
+    def _create_extra_rules(self, rules):
+        # This function generates extra rules for symbol specific rules. The reason for generating extra rules is to isolate,
+        # as much as possible, rules that require expansion. Particularly, object specific extra rules are generated.
+        rules_to_process = sorted(rules, key = lambda r: r.specificity)
+        symbol_specific_rules = list(filter(lambda r: r.specificity == PlacementRule.SYMBOL_SPECIFICITY, rules_to_process))
+
+        extra_rules = dict()
+
+        for symbol_specific_rule in symbol_specific_rules:
+            extra_rule_candidate = {s: None for s in symbol_specific_rule.get_section_names()}
+
+            super_rules = filter(lambda r: symbol_specific_rule.is_more_specific_rule_of(r), rules_to_process)
+
+            # Take a look at the existing rules that are more general than the current symbol-specific rule.
+            # Only generate an extra rule if there is no existing object specific rule for that section
+            for super_rule in super_rules:
+                intersections = symbol_specific_rule.get_sections_intersection(super_rule)
+                for intersection in intersections:
+                    if super_rule.specificity != PlacementRule.OBJECT_SPECIFICITY:
+                        extra_rule_candidate[intersection] = super_rule
+                    else:
+                        extra_rule_candidate[intersection] = None
+
+            # Generate the extra rules for the symbol specific rule section, keeping track of the generated extra rules
+            for (section, section_rule) in extra_rule_candidate.items():
+                if section_rule:
+                    extra_rule = None
+                    extra_rules_key = (symbol_specific_rule.archive, symbol_specific_rule.obj, section_rule.target)
+
+                    try:
+                        extra_rule = extra_rules[extra_rules_key]
+
+                        if section not in extra_rule.get_section_names():
+                            new_rule = PlacementRule(extra_rule.archive, extra_rule.obj, extra_rule.symbol, list(extra_rule.get_section_names()) + [section] , extra_rule.target)
+                            extra_rules[extra_rules_key] = new_rule
+                    except KeyError:
+                        extra_rule = PlacementRule(symbol_specific_rule.archive, symbol_specific_rule.obj, None, [section], section_rule.target)
+                        extra_rules[extra_rules_key] = extra_rule
+
+        return extra_rules.values()
+
+    def _create_exclusions(self, mapping_rules, default_rules, sections_info):
+        rules = list(default_rules)
+        rules.extend(mapping_rules)
+
+        extra_rules = self._create_extra_rules(rules)
+
+        mapping_rules.extend(extra_rules)
+        rules.extend(extra_rules)
+
+        # Sort the rules by means of how specific they are. Sort by specificity from lowest to highest
+        # * -> lib:* -> lib:obj -> lib:obj:symbol
+        sorted_rules = sorted(rules, key = lambda r: r.specificity)
+
+        # Now that the rules have been sorted, loop through each rule, and then loop
+        # through rules below it (higher indeces), adding exclusions whenever appropriate.
+        for general_rule in sorted_rules:
+            for specific_rule in reversed(sorted_rules):
+                if (specific_rule.specificity > general_rule.specificity and \
+                    specific_rule.specificity != PlacementRule.SYMBOL_SPECIFICITY) or \
+                    (specific_rule.specificity == PlacementRule.SYMBOL_SPECIFICITY and \
+                    general_rule.specificity == PlacementRule.OBJECT_SPECIFICITY):
+                    general_rule.add_exclusion(specific_rule, sections_info)
+
+    def add_fragments_from_file(self, fragment_file):
+        for fragment in fragment_file.fragments:
+            dict_to_append_to = None
+
+            if isinstance(fragment, Scheme):
+                dict_to_append_to = self.schemes
+            elif isinstance(fragment, Sections):
+                dict_to_append_to = self.sections
+            else:
+                dict_to_append_to = self.mappings
+
+            # Raise exception when the fragment of the same type is already in the stored fragments
+            if fragment.name in dict_to_append_to.keys():
+                stored = dict_to_append_to[fragment.name].path
+                new = fragment.path
+                message = "Duplicate definition of fragment '%s' found in %s and %s." % (fragment.name, stored, new)
+                raise GenerationException(message)
+
+            dict_to_append_to[fragment.name] = fragment
+
+"""
+Encapsulates a linker script template file. Finds marker syntax and handles replacement to generate the
+final output.
+"""
+class TemplateModel:
+
+    Marker = collections.namedtuple("Marker", "target indent rules")
+
+    def __init__(self, template_file):
+        self.members = []
+        self.file = os.path.realpath(template_file.name)
+
+        self._generate_members(template_file)
+
+    def _generate_members(self, template_file):
+        lines = template_file.readlines()
+
+        target = Fragment.IDENTIFIER
+        reference = Suppress("mapping") + Suppress("[") + target.setResultsName("target") + Suppress("]")
+        pattern = White(" \t").setResultsName("indent") + reference
+
+        # Find the markers in the template file line by line. If line does not match marker grammar,
+        # set it as a literal to be copied as is to the output file.
+        for line in lines:
+            try:
+                parsed = pattern.parseString(line)
+
+                indent = parsed.indent
+                target = parsed.target
+
+                marker = TemplateModel.Marker(target, indent, [])
+
+                self.members.append(marker)
+            except ParseException:
+                # Does not match marker syntax
+                self.members.append(line)
+
+    def fill(self, mapping_rules, sdkconfig):
+        for member in self.members:
+            target = None
+            try:
+                target = member.target
+                indent = member.indent
+                rules = member.rules
+
+                del rules[:]
+
+                rules.extend(mapping_rules[target])
+            except KeyError:
+                message = GenerationException.UNDEFINED_REFERENCE + " to target '" + target + "'."
+                raise GenerationException(message)
+            except AttributeError as a:
+                pass
+
+    def write(self, output_file):
+        # Add information that this is a generated file.
+        output_file.write("/* Automatically generated file; DO NOT EDIT */\n")
+        output_file.write("/* Espressif IoT Development Framework Linker Script */\n")
+        output_file.write("/* Generated from: %s */\n" % self.file)
+        output_file.write("\n")
+
+        # Do the text replacement
+        for member in self.members:
+            try:
+                indent = member.indent
+                rules = member.rules
+
+                for rule in rules:
+                    generated_line = "".join([indent, str(rule), '\n'])
+                    output_file.write(generated_line)
+            except AttributeError:
+                output_file.write(member)
+
+"""
+Exception for linker script generation failures such as undefined references/ failure to
+evaluate conditions, duplicate mappings, etc.
+"""
+class GenerationException(Exception):
+
+    UNDEFINED_REFERENCE = "Undefined reference"
+
+    def __init__(self, message, fragment=None):
+        self.fragment = fragment
+        self.message = message
+
+    def __str__(self):
+        if self.fragment:
+            return "%s\nIn fragment '%s' defined in '%s'." % (self.message, self.fragment.name, self.fragment.path)
+        else:
+            return self.message
+
+"""
+Encapsulates an output of objdump. Contains information about the static library sections
+and names
+"""
+class SectionsInfo(dict):
+
+    PATH = Optional("/") + ZeroOrMore(Regex(r"[^/.]+") + Literal("/"))
+
+    __info = collections.namedtuple("__info", "filename content")
+
+    def __init__(self):
+        self.sections = dict()
+
+    def add_sections_info(self, sections_info_file):
+        first_line = sections_info_file.readline()
+
+        archive = Literal("In archive").suppress() + SectionsInfo.PATH.suppress() + Fragment.ENTITY.setResultsName("archive") + Literal(":").suppress()
+        parser = archive
+
+        results = None
+
+        try:
+            results = parser.parseString(first_line)
+        except ParseException as p:
+            raise ParseException("File " + sections_info_file.name + " is not a valid sections info file. " + p.message)
+
+        self.sections[results.archive] = SectionsInfo.__info(sections_info_file.name, sections_info_file.read())
+
+    def _get_infos_from_file(self, info):
+        # Object file line: '{object}:  file format elf32-xtensa-le'
+        object = Fragment.ENTITY.setResultsName("object") + Literal(":").suppress() + Literal("file format elf32-xtensa-le").suppress()
+
+        # Sections table
+        header = Suppress(Literal("Sections:") + Literal("Idx") + Literal("Name") + Literal("Size") + Literal("VMA") + Literal("LMA") + Literal("File off") + Literal("Algn"))
+        entry = Word(nums).suppress() + Fragment.ENTITY + Suppress(OneOrMore(Word(alphanums, exact=8)) + Word(nums + "*") + ZeroOrMore(Word(alphas.upper()) + Optional(Literal(","))))
+
+        # Content is object file line + sections table
+        content = Group(object + header + Group(ZeroOrMore(entry)).setResultsName("sections"))
+
+        parser = Group(ZeroOrMore(content)).setResultsName("contents")
+
+        sections_info_text = info.content
+        results = None
+
+        try:
+            results = parser.parseString(sections_info_text)
+        except ParseException as p:
+            raise ParseException("Unable to parse section info file " + info.filename + ". " + p.message)
+
+        return results
+
+    def get_obj_sections(self, archive, obj):
+        stored = self.sections[archive]
+
+        # Parse the contents of the sections file
+        if not isinstance(stored, dict):
+            parsed = self._get_infos_from_file(stored)
+            stored = dict()
+            for content in parsed.contents:
+                sections = list(map(lambda s: s, content.sections))
+                stored[content.object] = sections
+            self.sections[archive] = stored
+
+        for obj_key in stored.keys():
+            if obj_key == obj + ".o" or obj_key == obj + ".c.obj":
+                return stored[obj_key]
diff --git a/tools/ldgen/ldgen.py b/tools/ldgen/ldgen.py
new file mode 100755 (executable)
index 0000000..bb70950
--- /dev/null
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+#
+# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import argparse
+import os
+
+from fragments import FragmentFileModel
+from sdkconfig import SDKConfig
+from generation import GenerationModel, TemplateModel, SectionsInfo
+
+def main():
+
+    argparser = argparse.ArgumentParser(description = "ESP-IDF linker script generator")
+
+    argparser.add_argument(
+        "--input", "-i",
+        help = "Linker template file",
+        type = argparse.FileType("r"))
+
+    argparser.add_argument(
+        "--fragments", "-f",
+        type = argparse.FileType("r"),
+        help = "Input fragment files",
+        nargs = "+")
+
+    argparser.add_argument(
+        "--sections", "-s",
+        type = argparse.FileType("r"),
+        help = "Library sections info",
+        nargs = "+")
+
+    argparser.add_argument(
+        "--output", "-o",
+        help = "Output linker script",
+        type = argparse.FileType("w"))
+
+    argparser.add_argument(
+        "--config", "-c",
+        help = "Project configuration",
+        type = argparse.FileType("r"))
+
+    argparser.add_argument(
+        "--kconfig", "-k",
+        help = "IDF Kconfig file",
+        type = argparse.FileType("r"))
+
+    argparser.add_argument(
+        "--env", "-e",
+        action='append', default=[],
+        help='Environment to set when evaluating the config file', metavar='NAME=VAL')
+
+    args = argparser.parse_args()
+
+    input_file = args.input
+    fragment_files = [] if not args.fragments else args.fragments
+    config_file = args.config
+    output_file = args.output
+    sections_info_files = [] if not args.sections else args.sections
+    kconfig_file = args.kconfig
+    
+    try:
+        sections_infos = SectionsInfo()
+
+        for sections_info_file in sections_info_files:
+            sections_infos.add_sections_info(sections_info_file)
+
+        generation_model = GenerationModel()
+
+        for fragment_file in fragment_files:
+            fragment_file = FragmentFileModel(fragment_file)
+            generation_model.add_fragments_from_file(fragment_file)
+
+        sdkconfig = SDKConfig(kconfig_file, config_file, args.env)
+        mapping_rules = generation_model.generate_rules(sdkconfig, sections_infos)
+
+        script_model = TemplateModel(input_file)
+        script_model.fill(mapping_rules, sdkconfig)
+
+        script_model.write(output_file)
+
+    except Exception, e:
+        print("linker script generation failed for %s\nERROR: %s" % (input_file.name, e.message))
+        # Delete the file so the entire build will fail; and not use an outdated script.
+        os.remove(output_file.name)
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/tools/ldgen/pyparsing.py b/tools/ldgen/pyparsing.py
new file mode 100644 (file)
index 0000000..892f339
--- /dev/null
@@ -0,0 +1,5720 @@
+# module pyparsing.py
+#
+# Copyright (c) 2003-2016  Paul T. McGuire
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__doc__ = \
+"""
+pyparsing module - Classes and methods to define and execute parsing grammars
+
+The pyparsing module is an alternative approach to creating and executing simple grammars,
+vs. the traditional lex/yacc approach, or the use of regular expressions.  With pyparsing, you
+don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
+provides a library of classes that you use to construct the grammar directly in Python.
+
+Here is a program to parse "Hello, World!" (or any greeting of the form
+C{"<salutation>, <addressee>!"}), built up using L{Word}, L{Literal}, and L{And} elements
+(L{'+'<ParserElement.__add__>} operator gives L{And} expressions, strings are auto-converted to
+L{Literal} expressions)::
+
+    from pyparsing import Word, alphas
+
+    # define grammar of a greeting
+    greet = Word(alphas) + "," + Word(alphas) + "!"
+
+    hello = "Hello, World!"
+    print (hello, "->", greet.parseString(hello))
+
+The program outputs the following::
+
+    Hello, World! -> ['Hello', ',', 'World', '!']
+
+The Python representation of the grammar is quite readable, owing to the self-explanatory
+class names, and the use of '+', '|' and '^' operators.
+
+The L{ParseResults} object returned from L{ParserElement.parseString<ParserElement.parseString>} can be accessed as a nested list, a dictionary, or an
+object with named attributes.
+
+The pyparsing module handles some of the problems that are typically vexing when writing text parsers:
+ - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello  ,  World  !", etc.)
+ - quoted strings
+ - embedded comments
+"""
+
+__version__ = "2.2.0"
+__versionTime__ = "06 Mar 2017 02:06 UTC"
+__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"
+
+import string
+from weakref import ref as wkref
+import copy
+import sys
+import warnings
+import re
+import sre_constants
+import collections
+import pprint
+import traceback
+import types
+from datetime import datetime
+
+try:
+    from _thread import RLock
+except ImportError:
+    from threading import RLock
+
+try:
+    from collections import OrderedDict as _OrderedDict
+except ImportError:
+    try:
+        from ordereddict import OrderedDict as _OrderedDict
+    except ImportError:
+        _OrderedDict = None
+
+#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) )
+
+__all__ = [
+'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
+'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
+'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
+'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
+'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
+'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter',
+'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore',
+'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
+'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
+'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums',
+'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno',
+'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
+'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
+'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity',
+'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
+'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
+'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass',
+'CloseMatch', 'tokenMap', 'pyparsing_common',
+]
+
+system_version = tuple(sys.version_info)[:3]
+PY_3 = system_version[0] == 3
+if PY_3:
+    _MAX_INT = sys.maxsize
+    basestring = str
+    unichr = chr
+    _ustr = str
+
+    # build list of single arg builtins, that can be used as parse actions
+    singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max]
+
+else:
+    _MAX_INT = sys.maxint
+    range = xrange
+
+    def _ustr(obj):
+        """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries
+           str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It
+           then < returns the unicode object | encodes it with the default encoding | ... >.
+        """
+        if isinstance(obj,unicode):
+            return obj
+
+        try:
+            # If this works, then _ustr(obj) has the same behaviour as str(obj), so
+            # it won't break any existing code.
+            return str(obj)
+
+        except UnicodeEncodeError:
+            # Else encode it
+            ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
+            xmlcharref = Regex(r'&#\d+;')
+            xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
+            return xmlcharref.transformString(ret)
+
+    # build list of single arg builtins, tolerant of Python version, that can be used as parse actions
+    singleArgBuiltins = []
+    import __builtin__
+    for fname in "sum len sorted reversed list tuple set any all min max".split():
+        try:
+            singleArgBuiltins.append(getattr(__builtin__,fname))
+        except AttributeError:
+            continue
+
+_generatorType = type((y for y in range(1)))
+
+def _xml_escape(data):
+    """Escape &, <, >, ", ', etc. in a string of data."""
+
+    # ampersand must be replaced first
+    from_symbols = '&><"\''
+    to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split())
+    for from_,to_ in zip(from_symbols, to_symbols):
+        data = data.replace(from_, to_)
+    return data
+
+class _Constants(object):
+    pass
+
+alphas     = string.ascii_uppercase + string.ascii_lowercase
+nums       = "0123456789"
+hexnums    = nums + "ABCDEFabcdef"
+alphanums  = alphas + nums
+_bslash    = chr(92)
+printables = "".join(c for c in string.printable if c not in string.whitespace)
+
+class ParseBaseException(Exception):
+    """base exception class for all parsing runtime exceptions"""
+    # Performance tuning: we construct a *lot* of these, so keep this
+    # constructor as small and fast as possible
+    def __init__( self, pstr, loc=0, msg=None, elem=None ):
+        self.loc = loc
+        if msg is None:
+            self.msg = pstr
+            self.pstr = ""
+        else:
+            self.msg = msg
+            self.pstr = pstr
+        self.parserElement = elem
+        self.args = (pstr, loc, msg)
+
+    @classmethod
+    def _from_exception(cls, pe):
+        """
+        internal factory method to simplify creating one type of ParseException
+        from another - avoids having __init__ signature conflicts among subclasses
+        """
+        return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
+
+    def __getattr__( self, aname ):
+        """supported attributes by name are:
+            - lineno - returns the line number of the exception text
+            - col - returns the column number of the exception text
+            - line - returns the line containing the exception text
+        """
+        if( aname == "lineno" ):
+            return lineno( self.loc, self.pstr )
+        elif( aname in ("col", "column") ):
+            return col( self.loc, self.pstr )
+        elif( aname == "line" ):
+            return line( self.loc, self.pstr )
+        else:
+            raise AttributeError(aname)
+
+    def __str__( self ):
+        return "%s (at char %d), (line:%d, col:%d)" % \
+                ( self.msg, self.loc, self.lineno, self.column )
+    def __repr__( self ):
+        return _ustr(self)
+    def markInputline( self, markerString = ">!<" ):
+        """Extracts the exception line from the input string, and marks
+           the location of the exception with a special symbol.
+        """
+        line_str = self.line
+        line_column = self.column - 1
+        if markerString:
+            line_str = "".join((line_str[:line_column],
+                                markerString, line_str[line_column:]))
+        return line_str.strip()
+    def __dir__(self):
+        return "lineno col line".split() + dir(type(self))
+
+class ParseException(ParseBaseException):
+    """
+    Exception thrown when parse expressions don't match class;
+    supported attributes by name are:
+     - lineno - returns the line number of the exception text
+     - col - returns the column number of the exception text
+     - line - returns the line containing the exception text
+
+    Example::
+        try:
+            Word(nums).setName("integer").parseString("ABC")
+        except ParseException as pe:
+            print(pe)
+            print("column: {}".format(pe.col))
+
+    prints::
+       Expected integer (at char 0), (line:1, col:1)
+        column: 1
+    """
+    pass
+
+class ParseFatalException(ParseBaseException):
+    """user-throwable exception thrown when inconsistent parse content
+       is found; stops all parsing immediately"""
+    pass
+
+class ParseSyntaxException(ParseFatalException):
+    """just like L{ParseFatalException}, but thrown internally when an
+       L{ErrorStop<And._ErrorStop>} ('-' operator) indicates that parsing is to stop
+       immediately because an unbacktrackable syntax error has been found"""
+    pass
+
+#~ class ReparseException(ParseBaseException):
+    #~ """Experimental class - parse actions can raise this exception to cause
+       #~ pyparsing to reparse the input string:
+        #~ - with a modified input string, and/or
+        #~ - with a modified start location
+       #~ Set the values of the ReparseException in the constructor, and raise the
+       #~ exception in a parse action to cause pyparsing to use the new string/location.
+       #~ Setting the values as None causes no change to be made.
+       #~ """
+    #~ def __init_( self, newstring, restartLoc ):
+        #~ self.newParseText = newstring
+        #~ self.reparseLoc = restartLoc
+
+class RecursiveGrammarException(Exception):
+    """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive"""
+    def __init__( self, parseElementList ):
+        self.parseElementTrace = parseElementList
+
+    def __str__( self ):
+        return "RecursiveGrammarException: %s" % self.parseElementTrace
+
+class _ParseResultsWithOffset(object):
+    def __init__(self,p1,p2):
+        self.tup = (p1,p2)
+    def __getitem__(self,i):
+        return self.tup[i]
+    def __repr__(self):
+        return repr(self.tup[0])
+    def setOffset(self,i):
+        self.tup = (self.tup[0],i)
+
+class ParseResults(object):
+    """
+    Structured parse results, to provide multiple means of access to the parsed data:
+       - as a list (C{len(results)})
+       - by list index (C{results[0], results[1]}, etc.)
+       - by attribute (C{results.<resultsName>} - see L{ParserElement.setResultsName})
+
+    Example::
+        integer = Word(nums)
+        date_str = (integer.setResultsName("year") + '/'
+                        + integer.setResultsName("month") + '/'
+                        + integer.setResultsName("day"))
+        # equivalent form:
+        # date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+
+        # parseString returns a ParseResults object
+        result = date_str.parseString("1999/12/31")
+
+        def test(s, fn=repr):
+            print("%s -> %s" % (s, fn(eval(s))))
+        test("list(result)")
+        test("result[0]")
+        test("result['month']")
+        test("result.day")
+        test("'month' in result")
+        test("'minutes' in result")
+        test("result.dump()", str)
+    prints::
+        list(result) -> ['1999', '/', '12', '/', '31']
+        result[0] -> '1999'
+        result['month'] -> '12'
+        result.day -> '31'
+        'month' in result -> True
+        'minutes' in result -> False
+        result.dump() -> ['1999', '/', '12', '/', '31']
+        - day: 31
+        - month: 12
+        - year: 1999
+    """
+    def __new__(cls, toklist=None, name=None, asList=True, modal=True ):
+        if isinstance(toklist, cls):
+            return toklist
+        retobj = object.__new__(cls)
+        retobj.__doinit = True
+        return retobj
+
+    # Performance tuning: we construct a *lot* of these, so keep this
+    # constructor as small and fast as possible
+    def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ):
+        if self.__doinit:
+            self.__doinit = False
+            self.__name = None
+            self.__parent = None
+            self.__accumNames = {}
+            self.__asList = asList
+            self.__modal = modal
+            if toklist is None:
+                toklist = []
+            if isinstance(toklist, list):
+                self.__toklist = toklist[:]
+            elif isinstance(toklist, _generatorType):
+                self.__toklist = list(toklist)
+            else:
+                self.__toklist = [toklist]
+            self.__tokdict = dict()
+
+        if name is not None and name:
+            if not modal:
+                self.__accumNames[name] = 0
+            if isinstance(name,int):
+                name = _ustr(name) # will always return a str, but use _ustr for consistency
+            self.__name = name
+            if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])):
+                if isinstance(toklist,basestring):
+                    toklist = [ toklist ]
+                if asList:
+                    if isinstance(toklist,ParseResults):
+                        self[name] = _ParseResultsWithOffset(toklist.copy(),0)
+                    else:
+                        self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0)
+                    self[name].__name = name
+                else:
+                    try:
+                        self[name] = toklist[0]
+                    except (KeyError,TypeError,IndexError):
+                        self[name] = toklist
+
+    def __getitem__( self, i ):
+        if isinstance( i, (int,slice) ):
+            return self.__toklist[i]
+        else:
+            if i not in self.__accumNames:
+                return self.__tokdict[i][-1][0]
+            else:
+                return ParseResults([ v[0] for v in self.__tokdict[i] ])
+
+    def __setitem__( self, k, v, isinstance=isinstance ):
+        if isinstance(v,_ParseResultsWithOffset):
+            self.__tokdict[k] = self.__tokdict.get(k,list()) + [v]
+            sub = v[0]
+        elif isinstance(k,(int,slice)):
+            self.__toklist[k] = v
+            sub = v
+        else:
+            self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)]
+            sub = v
+        if isinstance(sub,ParseResults):
+            sub.__parent = wkref(self)
+
+    def __delitem__( self, i ):
+        if isinstance(i,(int,slice)):
+            mylen = len( self.__toklist )
+            del self.__toklist[i]
+
+            # convert int to slice
+            if isinstance(i, int):
+                if i < 0:
+                    i += mylen
+                i = slice(i, i+1)
+            # get removed indices
+            removed = list(range(*i.indices(mylen)))
+            removed.reverse()
+            # fixup indices in token dictionary
+            for name,occurrences in self.__tokdict.items():
+                for j in removed:
+                    for k, (value, position) in enumerate(occurrences):
+                        occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
+        else:
+            del self.__tokdict[i]
+
+    def __contains__( self, k ):
+        return k in self.__tokdict
+
+    def __len__( self ): return len( self.__toklist )
+    def __bool__(self): return ( not not self.__toklist )
+    __nonzero__ = __bool__
+    def __iter__( self ): return iter( self.__toklist )
+    def __reversed__( self ): return iter( self.__toklist[::-1] )
+    def _iterkeys( self ):
+        if hasattr(self.__tokdict, "iterkeys"):
+            return self.__tokdict.iterkeys()
+        else:
+            return iter(self.__tokdict)
+
+    def _itervalues( self ):
+        return (self[k] for k in self._iterkeys())
+
+    def _iteritems( self ):
+        return ((k, self[k]) for k in self._iterkeys())
+
+    if PY_3:
+        keys = _iterkeys
+        """Returns an iterator of all named result keys (Python 3.x only)."""
+
+        values = _itervalues
+        """Returns an iterator of all named result values (Python 3.x only)."""
+
+        items = _iteritems
+        """Returns an iterator of all named result key-value tuples (Python 3.x only)."""
+
+    else:
+        iterkeys = _iterkeys
+        """Returns an iterator of all named result keys (Python 2.x only)."""
+
+        itervalues = _itervalues
+        """Returns an iterator of all named result values (Python 2.x only)."""
+
+        iteritems = _iteritems
+        """Returns an iterator of all named result key-value tuples (Python 2.x only)."""
+
+        def keys( self ):
+            """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.iterkeys())
+
+        def values( self ):
+            """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.itervalues())
+
+        def items( self ):
+            """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.iteritems())
+
+    def haskeys( self ):
+        """Since keys() returns an iterator, this method is helpful in bypassing
+           code that looks for the existence of any defined results names."""
+        return bool(self.__tokdict)
+
+    def pop( self, *args, **kwargs):
+        """
+        Removes and returns item at specified index (default=C{last}).
+        Supports both C{list} and C{dict} semantics for C{pop()}. If passed no
+        argument or an integer argument, it will use C{list} semantics
+        and pop tokens from the list of parsed tokens. If passed a
+        non-integer argument (most likely a string), it will use C{dict}
+        semantics and pop the corresponding value from any defined
+        results names. A second default return value argument is
+        supported, just as in C{dict.pop()}.
+
+        Example::
+            def remove_first(tokens):
+                tokens.pop(0)
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+            print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321']
+
+            label = Word(alphas)
+            patt = label("LABEL") + OneOrMore(Word(nums))
+            print(patt.parseString("AAB 123 321").dump())
+
+            # Use pop() in a parse action to remove named result (note that corresponding value is not
+            # removed from list form of results)
+            def remove_LABEL(tokens):
+                tokens.pop("LABEL")
+                return tokens
+            patt.addParseAction(remove_LABEL)
+            print(patt.parseString("AAB 123 321").dump())
+        prints::
+            ['AAB', '123', '321']
+            - LABEL: AAB
+
+            ['AAB', '123', '321']
+        """
+        if not args:
+            args = [-1]
+        for k,v in kwargs.items():
+            if k == 'default':
+                args = (args[0], v)
+            else:
+                raise TypeError("pop() got an unexpected keyword argument '%s'" % k)
+        if (isinstance(args[0], int) or
+                        len(args) == 1 or
+                        args[0] in self):
+            index = args[0]
+            ret = self[index]
+            del self[index]
+            return ret
+        else:
+            defaultvalue = args[1]
+            return defaultvalue
+
+    def get(self, key, defaultValue=None):
+        """
+        Returns named result matching the given key, or if there is no
+        such name, then returns the given C{defaultValue} or C{None} if no
+        C{defaultValue} is specified.
+
+        Similar to C{dict.get()}.
+
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+
+            result = date_str.parseString("1999/12/31")
+            print(result.get("year")) # -> '1999'
+            print(result.get("hour", "not specified")) # -> 'not specified'
+            print(result.get("hour")) # -> None
+        """
+        if key in self:
+            return self[key]
+        else:
+            return defaultValue
+
+    def insert( self, index, insStr ):
+        """
+        Inserts new element at location index in the list of parsed tokens.
+
+        Similar to C{list.insert()}.
+
+        Example::
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+
+            # use a parse action to insert the parse location in the front of the parsed results
+            def insert_locn(locn, tokens):
+                tokens.insert(0, locn)
+            print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321']
+        """
+        self.__toklist.insert(index, insStr)
+        # fixup indices in token dictionary
+        for name,occurrences in self.__tokdict.items():
+            for k, (value, position) in enumerate(occurrences):
+                occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
+
+    def append( self, item ):
+        """
+        Add single element to end of ParseResults list of elements.
+
+        Example::
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+
+            # use a parse action to compute the sum of the parsed integers, and add it to the end
+            def append_sum(tokens):
+                tokens.append(sum(map(int, tokens)))
+            print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444]
+        """
+        self.__toklist.append(item)
+
+    def extend( self, itemseq ):
+        """
+        Add sequence of elements to end of ParseResults list of elements.
+
+        Example::
+            patt = OneOrMore(Word(alphas))
+
+            # use a parse action to append the reverse of the matched strings, to make a palindrome
+            def make_palindrome(tokens):
+                tokens.extend(reversed([t[::-1] for t in tokens]))
+                return ''.join(tokens)
+            print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
+        """
+        if isinstance(itemseq, ParseResults):
+            self += itemseq
+        else:
+            self.__toklist.extend(itemseq)
+
+    def clear( self ):
+        """
+        Clear all elements and results names.
+        """
+        del self.__toklist[:]
+        self.__tokdict.clear()
+
+    def __getattr__( self, name ):
+        try:
+            return self[name]
+        except KeyError:
+            return ""
+
+        if name in self.__tokdict:
+            if name not in self.__accumNames:
+                return self.__tokdict[name][-1][0]
+            else:
+                return ParseResults([ v[0] for v in self.__tokdict[name] ])
+        else:
+            return ""
+
+    def __add__( self, other ):
+        ret = self.copy()
+        ret += other
+        return ret
+
+    def __iadd__( self, other ):
+        if other.__tokdict:
+            offset = len(self.__toklist)
+            addoffset = lambda a: offset if a<0 else a+offset
+            otheritems = other.__tokdict.items()
+            otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) )
+                                for (k,vlist) in otheritems for v in vlist]
+            for k,v in otherdictitems:
+                self[k] = v
+                if isinstance(v[0],ParseResults):
+                    v[0].__parent = wkref(self)
+
+        self.__toklist += other.__toklist
+        self.__accumNames.update( other.__accumNames )
+        return self
+
+    def __radd__(self, other):
+        if isinstance(other,int) and other == 0:
+            # useful for merging many ParseResults using sum() builtin
+            return self.copy()
+        else:
+            # this may raise a TypeError - so be it
+            return other + self
+
+    def __repr__( self ):
+        return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) )
+
+    def __str__( self ):
+        return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']'
+
+    def _asStringList( self, sep='' ):
+        out = []
+        for item in self.__toklist:
+            if out and sep:
+                out.append(sep)
+            if isinstance( item, ParseResults ):
+                out += item._asStringList()
+            else:
+                out.append( _ustr(item) )
+        return out
+
+    def asList( self ):
+        """
+        Returns the parse results as a nested list of matching tokens, all converted to strings.
+
+        Example::
+            patt = OneOrMore(Word(alphas))
+            result = patt.parseString("sldkj lsdkj sldkj")
+            # even though the result prints in string-like form, it is actually a pyparsing ParseResults
+            print(type(result), result) # -> <class 'pyparsing.ParseResults'> ['sldkj', 'lsdkj', 'sldkj']
+
+            # Use asList() to create an actual list
+            result_list = result.asList()
+            print(type(result_list), result_list) # -> <class 'list'> ['sldkj', 'lsdkj', 'sldkj']
+        """
+        return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist]
+
+    def asDict( self ):
+        """
+        Returns the named parse results as a nested dictionary.
+
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+
+            result = date_str.parseString('12/31/1999')
+            print(type(result), repr(result)) # -> <class 'pyparsing.ParseResults'> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
+
+            result_dict = result.asDict()
+            print(type(result_dict), repr(result_dict)) # -> <class 'dict'> {'day': '1999', 'year': '12', 'month': '31'}
+
+            # even though a ParseResults supports dict-like access, sometime you just need to have a dict
+            import json
+            print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
+            print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"}
+        """
+        if PY_3:
+            item_fn = self.items
+        else:
+            item_fn = self.iteritems
+
+        def toItem(obj):
+            if isinstance(obj, ParseResults):
+                if obj.haskeys():
+                    return obj.asDict()
+                else:
+                    return [toItem(v) for v in obj]
+            else:
+                return obj
+
+        return dict((k,toItem(v)) for k,v in item_fn())
+
+    def copy( self ):
+        """
+        Returns a new copy of a C{ParseResults} object.
+        """
+        ret = ParseResults( self.__toklist )
+        ret.__tokdict = self.__tokdict.copy()
+        ret.__parent = self.__parent
+        ret.__accumNames.update( self.__accumNames )
+        ret.__name = self.__name
+        return ret
+
+    def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ):
+        """
+        (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.
+        """
+        nl = "\n"
+        out = []
+        namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items()
+                                                            for v in vlist)
+        nextLevelIndent = indent + "  "
+
+        # collapse out indents if formatting is not desired
+        if not formatted:
+            indent = ""
+            nextLevelIndent = ""
+            nl = ""
+
+        selfTag = None
+        if doctag is not None:
+            selfTag = doctag
+        else:
+            if self.__name:
+                selfTag = self.__name
+
+        if not selfTag:
+            if namedItemsOnly:
+                return ""
+            else:
+                selfTag = "ITEM"
+
+        out += [ nl, indent, "<", selfTag, ">" ]
+
+        for i,res in enumerate(self.__toklist):
+            if isinstance(res,ParseResults):
+                if i in namedItems:
+                    out += [ res.asXML(namedItems[i],
+                                        namedItemsOnly and doctag is None,
+                                        nextLevelIndent,
+                                        formatted)]
+                else:
+                    out += [ res.asXML(None,
+                                        namedItemsOnly and doctag is None,
+                                        nextLevelIndent,
+                                        formatted)]
+            else:
+                # individual token, see if there is a name for it
+                resTag = None
+                if i in namedItems:
+                    resTag = namedItems[i]
+                if not resTag:
+                    if namedItemsOnly:
+                        continue
+                    else:
+                        resTag = "ITEM"
+                xmlBodyText = _xml_escape(_ustr(res))
+                out += [ nl, nextLevelIndent, "<", resTag, ">",
+                                                xmlBodyText,
+                                                "</", resTag, ">" ]
+
+        out += [ nl, indent, "</", selfTag, ">" ]
+        return "".join(out)
+
+    def __lookup(self,sub):
+        for k,vlist in self.__tokdict.items():
+            for v,loc in vlist:
+                if sub is v:
+                    return k
+        return None
+
+    def getName(self):
+        r"""
+        Returns the results name for this token expression. Useful when several
+        different expressions might match at a particular location.
+
+        Example::
+            integer = Word(nums)
+            ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
+            house_number_expr = Suppress('#') + Word(nums, alphanums)
+            user_data = (Group(house_number_expr)("house_number")
+                        | Group(ssn_expr)("ssn")
+                        | Group(integer)("age"))
+            user_info = OneOrMore(user_data)
+
+            result = user_info.parseString("22 111-22-3333 #221B")
+            for item in result:
+                print(item.getName(), ':', item[0])
+        prints::
+            age : 22
+            ssn : 111-22-3333
+            house_number : 221B
+        """
+        if self.__name:
+            return self.__name
+        elif self.__parent:
+            par = self.__parent()
+            if par:
+                return par.__lookup(self)
+            else:
+                return None
+        elif (len(self) == 1 and
+               len(self.__tokdict) == 1 and
+               next(iter(self.__tokdict.values()))[0][1] in (0,-1)):
+            return next(iter(self.__tokdict.keys()))
+        else:
+            return None
+
+    def dump(self, indent='', depth=0, full=True):
+        """
+        Diagnostic method for listing out the contents of a C{ParseResults}.
+        Accepts an optional C{indent} argument so that this string can be embedded
+        in a nested display of other data.
+
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+
+            result = date_str.parseString('12/31/1999')
+            print(result.dump())
+        prints::
+            ['12', '/', '31', '/', '1999']
+            - day: 1999
+            - month: 31
+            - year: 12
+        """
+        out = []
+        NL = '\n'
+        out.append( indent+_ustr(self.asList()) )
+        if full:
+            if self.haskeys():
+                items = sorted((str(k), v) for k,v in self.items())
+                for k,v in items:
+                    if out:
+                        out.append(NL)
+                    out.append( "%s%s- %s: " % (indent,('  '*depth), k) )
+                    if isinstance(v,ParseResults):
+                        if v:
+                            out.append( v.dump(indent,depth+1) )
+                        else:
+                            out.append(_ustr(v))
+                    else:
+                        out.append(repr(v))
+            elif any(isinstance(vv,ParseResults) for vv in self):
+                v = self
+                for i,vv in enumerate(v):
+                    if isinstance(vv,ParseResults):
+                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),vv.dump(indent,depth+1) ))
+                    else:
+                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),_ustr(vv)))
+
+        return "".join(out)
+
+    def pprint(self, *args, **kwargs):
+        """
+        Pretty-printer for parsed results as a list, using the C{pprint} module.
+        Accepts additional positional or keyword args as defined for the
+        C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})
+
+        Example::
+            ident = Word(alphas, alphanums)
+            num = Word(nums)
+            func = Forward()
+            term = ident | num | Group('(' + func + ')')
+            func <<= ident + Group(Optional(delimitedList(term)))
+            result = func.parseString("fna a,b,(fnb c,d,200),100")
+            result.pprint(width=40)
+        prints::
+            ['fna',
+             ['a',
+              'b',
+              ['(', 'fnb', ['c', 'd', '200'], ')'],
+              '100']]
+        """
+        pprint.pprint(self.asList(), *args, **kwargs)
+
+    # add support for pickle protocol
+    def __getstate__(self):
+        return ( self.__toklist,
+                 ( self.__tokdict.copy(),
+                   self.__parent is not None and self.__parent() or None,
+                   self.__accumNames,
+                   self.__name ) )
+
+    def __setstate__(self,state):
+        self.__toklist = state[0]
+        (self.__tokdict,
+         par,
+         inAccumNames,
+         self.__name) = state[1]
+        self.__accumNames = {}
+        self.__accumNames.update(inAccumNames)
+        if par is not None:
+            self.__parent = wkref(par)
+        else:
+            self.__parent = None
+
+    def __getnewargs__(self):
+        return self.__toklist, self.__name, self.__asList, self.__modal
+
+    def __dir__(self):
+        return (dir(type(self)) + list(self.keys()))
+
+collections.MutableMapping.register(ParseResults)
+
+def col (loc,strg):
+    """Returns current column within a string, counting newlines as line separators.
+   The first column is number 1.
+
+   Note: the default parsing behavior is to expand tabs in the input string
+   before starting the parsing process.  See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information
+   on parsing strings containing C{<TAB>}s, and suggested methods to maintain a
+   consistent view of the parsed string, the parse location, and line and column
+   positions within the parsed string.
+   """
+    s = strg
+    return 1 if 0<loc<len(s) and s[loc-1] == '\n' else loc - s.rfind("\n", 0, loc)
+
+def lineno(loc,strg):
+    """Returns current line number within a string, counting newlines as line separators.
+   The first line is number 1.
+
+   Note: the default parsing behavior is to expand tabs in the input string
+   before starting the parsing process.  See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information
+   on parsing strings containing C{<TAB>}s, and suggested methods to maintain a
+   consistent view of the parsed string, the parse location, and line and column
+   positions within the parsed string.
+   """
+    return strg.count("\n",0,loc) + 1
+
+def line( loc, strg ):
+    """Returns the line of text containing loc within a string, counting newlines as line separators.
+       """
+    lastCR = strg.rfind("\n", 0, loc)
+    nextCR = strg.find("\n", loc)
+    if nextCR >= 0:
+        return strg[lastCR+1:nextCR]
+    else:
+        return strg[lastCR+1:]
+
+def _defaultStartDebugAction( instring, loc, expr ):
+    print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )))
+
+def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
+    print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
+
+def _defaultExceptionDebugAction( instring, loc, expr, exc ):
+    print ("Exception raised:" + _ustr(exc))
+
+def nullDebugAction(*args):
+    """'Do-nothing' debug action, to suppress debugging output during parsing."""
+    pass
+
+# Only works on Python 3.x - nonlocal is toxic to Python 2 installs
+#~ 'decorator to trim function calls to match the arity of the target'
+#~ def _trim_arity(func, maxargs=3):
+    #~ if func in singleArgBuiltins:
+        #~ return lambda s,l,t: func(t)
+    #~ limit = 0
+    #~ foundArity = False
+    #~ def wrapper(*args):
+        #~ nonlocal limit,foundArity
+        #~ while 1:
+            #~ try:
+                #~ ret = func(*args[limit:])
+                #~ foundArity = True
+                #~ return ret
+            #~ except TypeError:
+                #~ if limit == maxargs or foundArity:
+                    #~ raise
+                #~ limit += 1
+                #~ continue
+    #~ return wrapper
+
+# this version is Python 2.x-3.x cross-compatible
+'decorator to trim function calls to match the arity of the target'
+def _trim_arity(func, maxargs=2):
+    if func in singleArgBuiltins:
+        return lambda s,l,t: func(t)
+    limit = [0]
+    foundArity = [False]
+
+    # traceback return data structure changed in Py3.5 - normalize back to plain tuples
+    if system_version[:2] >= (3,5):
+        def extract_stack(limit=0):
+            # special handling for Python 3.5.0 - extra deep call stack by 1
+            offset = -3 if system_version == (3,5,0) else -2
+            frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset]
+            return [(frame_summary.filename, frame_summary.lineno)]
+        def extract_tb(tb, limit=0):
+            frames = traceback.extract_tb(tb, limit=limit)
+            frame_summary = frames[-1]
+            return [(frame_summary.filename, frame_summary.lineno)]
+    else:
+        extract_stack = traceback.extract_stack
+        extract_tb = traceback.extract_tb
+
+    # synthesize what would be returned by traceback.extract_stack at the call to
+    # user's parse action 'func', so that we don't incur call penalty at parse time
+
+    LINE_DIFF = 6
+    # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND
+    # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!!
+    this_line = extract_stack(limit=2)[-1]
+    pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF)
+
+    def wrapper(*args):
+        while 1:
+            try:
+                ret = func(*args[limit[0]:])
+                foundArity[0] = True
+                return ret
+            except TypeError:
+                # re-raise TypeErrors if they did not come from our arity testing
+                if foundArity[0]:
+                    raise
+                else:
+                    try:
+                        tb = sys.exc_info()[-1]
+                        if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth:
+                            raise
+                    finally:
+                        del tb
+
+                if limit[0] <= maxargs:
+                    limit[0] += 1
+                    continue
+                raise
+
+    # copy func name to wrapper for sensible debug output
+    func_name = "<parse action>"
+    try:
+        func_name = getattr(func, '__name__',
+                            getattr(func, '__class__').__name__)
+    except Exception:
+        func_name = str(func)
+    wrapper.__name__ = func_name
+
+    return wrapper
+
+class ParserElement(object):
+    """Abstract base level parser element class."""
+    DEFAULT_WHITE_CHARS = " \n\t\r"
+    verbose_stacktrace = False
+
+    @staticmethod
+    def setDefaultWhitespaceChars( chars ):
+        r"""
+        Overrides the default whitespace chars
+
+        Example::
+            # default whitespace chars are space, <TAB> and newline
+            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def', 'ghi', 'jkl']
+
+            # change to just treat newline as significant
+            ParserElement.setDefaultWhitespaceChars(" \t")
+            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def']
+        """
+        ParserElement.DEFAULT_WHITE_CHARS = chars
+
+    @staticmethod
+    def inlineLiteralsUsing(cls):
+        """
+        Set class to be used for inclusion of string literals into a parser.
+
+        Example::
+            # default literal class used is Literal
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
+
+
+            # change to Suppress
+            ParserElement.inlineLiteralsUsing(Suppress)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '12', '31']
+        """
+        ParserElement._literalStringClass = cls
+
+    def __init__( self, savelist=False ):
+        self.parseAction = list()
+        self.failAction = None
+        #~ self.name = "<unknown>"  # don't define self.name, let subclasses try/except upcall
+        self.strRepr = None
+        self.resultsName = None
+        self.saveAsList = savelist
+        self.skipWhitespace = True
+        self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
+        self.copyDefaultWhiteChars = True
+        self.mayReturnEmpty = False # used when checking for left-recursion
+        self.keepTabs = False
+        self.ignoreExprs = list()
+        self.debug = False
+        self.streamlined = False
+        self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
+        self.errmsg = ""
+        self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
+        self.debugActions = ( None, None, None ) #custom debug actions
+        self.re = None
+        self.callPreparse = True # used to avoid redundant calls to preParse
+        self.callDuringTry = False
+
+    def copy( self ):
+        """
+        Make a copy of this C{ParserElement}.  Useful for defining different parse actions
+        for the same parsing pattern, using copies of the original parse element.
+
+        Example::
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K")
+            integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
+
+            print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M"))
+        prints::
+            [5120, 100, 655360, 268435456]
+        Equivalent form of C{expr.copy()} is just C{expr()}::
+            integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
+        """
+        cpy = copy.copy( self )
+        cpy.parseAction = self.parseAction[:]
+        cpy.ignoreExprs = self.ignoreExprs[:]
+        if self.copyDefaultWhiteChars:
+            cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
+        return cpy
+
+    def setName( self, name ):
+        """
+        Define name for this expression, makes debugging and exception messages clearer.
+
+        Example::
+            Word(nums).parseString("ABC")  # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1)
+            Word(nums).setName("integer").parseString("ABC")  # -> Exception: Expected integer (at char 0), (line:1, col:1)
+        """
+        self.name = name
+        self.errmsg = "Expected " + self.name
+        if hasattr(self,"exception"):
+            self.exception.msg = self.errmsg
+        return self
+
+    def setResultsName( self, name, listAllMatches=False ):
+        """
+        Define name for referencing matching tokens as a nested attribute
+        of the returned parse results.
+        NOTE: this returns a *copy* of the original C{ParserElement} object;
+        this is so that the client can define a basic element, such as an
+        integer, and reference it in multiple places with different names.
+
+        You can also set results names using the abbreviated syntax,
+        C{expr("name")} in place of C{expr.setResultsName("name")} -
+        see L{I{__call__}<__call__>}.
+
+        Example::
+            date_str = (integer.setResultsName("year") + '/'
+                        + integer.setResultsName("month") + '/'
+                        + integer.setResultsName("day"))
+
+            # equivalent form:
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+        """
+        newself = self.copy()
+        if name.endswith("*"):
+            name = name[:-1]
+            listAllMatches=True
+        newself.resultsName = name
+        newself.modalResults = not listAllMatches
+        return newself
+
+    def setBreak(self,breakFlag = True):
+        """Method to invoke the Python pdb debugger when this element is
+           about to be parsed. Set C{breakFlag} to True to enable, False to
+           disable.
+        """
+        if breakFlag:
+            _parseMethod = self._parse
+            def breaker(instring, loc, doActions=True, callPreParse=True):
+                import pdb
+                pdb.set_trace()
+                return _parseMethod( instring, loc, doActions, callPreParse )
+            breaker._originalParseMethod = _parseMethod
+            self._parse = breaker
+        else:
+            if hasattr(self._parse,"_originalParseMethod"):
+                self._parse = self._parse._originalParseMethod
+        return self
+
+    def setParseAction( self, *fns, **kwargs ):
+        """
+        Define one or more actions to perform when successfully matching parse element definition.
+        Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
+        C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
+         - s   = the original string being parsed (see note below)
+         - loc = the location of the matching substring
+         - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object
+        If the functions in fns modify the tokens, they can return them as the return
+        value from fn, and the modified list of tokens will replace the original.
+        Otherwise, fn does not need to return any value.
+
+        Optional keyword arguments:
+         - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing
+
+        Note: the default parsing behavior is to expand tabs in the input string
+        before starting the parsing process.  See L{I{parseString}<parseString>} for more information
+        on parsing strings containing C{<TAB>}s, and suggested methods to maintain a
+        consistent view of the parsed string, the parse location, and line and column
+        positions within the parsed string.
+
+        Example::
+            integer = Word(nums)
+            date_str = integer + '/' + integer + '/' + integer
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
+
+            # use parse action to convert to ints at parse time
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            date_str = integer + '/' + integer + '/' + integer
+
+            # note that integer fields are now ints, not strings
+            date_str.parseString("1999/12/31")  # -> [1999, '/', 12, '/', 31]
+        """
+        self.parseAction = list(map(_trim_arity, list(fns)))
+        self.callDuringTry = kwargs.get("callDuringTry", False)
+        return self
+
+    def addParseAction( self, *fns, **kwargs ):
+        """
+        Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}.
+
+        See examples in L{I{copy}<copy>}.
+        """
+        self.parseAction += list(map(_trim_arity, list(fns)))
+        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
+        return self
+
+    def addCondition(self, *fns, **kwargs):
+        """Add a boolean predicate function to expression's list of parse actions. See
+        L{I{setParseAction}<setParseAction>} for function call signatures. Unlike C{setParseAction},
+        functions passed to C{addCondition} need to return boolean success/fail of the condition.
+
+        Optional keyword arguments:
+         - message = define a custom message to be used in the raised exception
+         - fatal   = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException
+
+        Example::
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            year_int = integer.copy()
+            year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later")
+            date_str = year_int + '/' + integer + '/' + integer
+
+            result = date_str.parseString("1999/12/31")  # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1)
+        """
+        msg = kwargs.get("message", "failed user-defined condition")
+        exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException
+        for fn in fns:
+            def pa(s,l,t):
+                if not bool(_trim_arity(fn)(s,l,t)):
+                    raise exc_type(s,l,msg)
+            self.parseAction.append(pa)
+        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
+        return self
+
+    def setFailAction( self, fn ):
+        """Define action to perform if parsing fails at this expression.
+           Fail acton fn is a callable function that takes the arguments
+           C{fn(s,loc,expr,err)} where:
+            - s = string being parsed
+            - loc = location where expression match was attempted and failed
+            - expr = the parse expression that failed
+            - err = the exception thrown
+           The function returns no value.  It may throw C{L{ParseFatalException}}
+           if it is desired to stop parsing immediately."""
+        self.failAction = fn
+        return self
+
+    def _skipIgnorables( self, instring, loc ):
+        exprsFound = True
+        while exprsFound:
+            exprsFound = False
+            for e in self.ignoreExprs:
+                try:
+                    while 1:
+                        loc,dummy = e._parse( instring, loc )
+                        exprsFound = True
+                except ParseException:
+                    pass
+        return loc
+
+    def preParse( self, instring, loc ):
+        if self.ignoreExprs:
+            loc = self._skipIgnorables( instring, loc )
+
+        if self.skipWhitespace:
+            wt = self.whiteChars
+            instrlen = len(instring)
+            while loc < instrlen and instring[loc] in wt:
+                loc += 1
+
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        return loc, []
+
+    def postParse( self, instring, loc, tokenlist ):
+        return tokenlist
+
+    #~ @profile
+    def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ):
+        debugging = ( self.debug ) #and doActions )
+
+        if debugging or self.failAction:
+            #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
+            if (self.debugActions[0] ):
+                self.debugActions[0]( instring, loc, self )
+            if callPreParse and self.callPreparse:
+                preloc = self.preParse( instring, loc )
+            else:
+                preloc = loc
+            tokensStart = preloc
+            try:
+                try:
+                    loc,tokens = self.parseImpl( instring, preloc, doActions )
+                except IndexError:
+                    raise ParseException( instring, len(instring), self.errmsg, self )
+            except ParseBaseException as err:
+                #~ print ("Exception raised:", err)
+                if self.debugActions[2]:
+                    self.debugActions[2]( instring, tokensStart, self, err )
+                if self.failAction:
+                    self.failAction( instring, tokensStart, self, err )
+                raise
+        else:
+            if callPreParse and self.callPreparse:
+                preloc = self.preParse( instring, loc )
+            else:
+                preloc = loc
+            tokensStart = preloc
+            if self.mayIndexError or loc >= len(instring):
+                try:
+                    loc,tokens = self.parseImpl( instring, preloc, doActions )
+                except IndexError:
+                    raise ParseException( instring, len(instring), self.errmsg, self )
+            else:
+                loc,tokens = self.parseImpl( instring, preloc, doActions )
+
+        tokens = self.postParse( instring, loc, tokens )
+
+        retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults )
+        if self.parseAction and (doActions or self.callDuringTry):
+            if debugging:
+                try:
+                    for fn in self.parseAction:
+                        tokens = fn( instring, tokensStart, retTokens )
+                        if tokens is not None:
+                            retTokens = ParseResults( tokens,
+                                                      self.resultsName,
+                                                      asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
+                                                      modal=self.modalResults )
+                except ParseBaseException as err:
+                    #~ print "Exception raised in user parse action:", err
+                    if (self.debugActions[2] ):
+                        self.debugActions[2]( instring, tokensStart, self, err )
+                    raise
+            else:
+                for fn in self.parseAction:
+                    tokens = fn( instring, tokensStart, retTokens )
+                    if tokens is not None:
+                        retTokens = ParseResults( tokens,
+                                                  self.resultsName,
+                                                  asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
+                                                  modal=self.modalResults )
+
+        if debugging:
+            #~ print ("Matched",self,"->",retTokens.asList())
+            if (self.debugActions[1] ):
+                self.debugActions[1]( instring, tokensStart, loc, self, retTokens )
+
+        return loc, retTokens
+
+    def tryParse( self, instring, loc ):
+        try:
+            return self._parse( instring, loc, doActions=False )[0]
+        except ParseFatalException:
+            raise ParseException( instring, loc, self.errmsg, self)
+
+    def canParseNext(self, instring, loc):
+        try:
+            self.tryParse(instring, loc)
+        except (ParseException, IndexError):
+            return False
+        else:
+            return True
+
+    class _UnboundedCache(object):
+        def __init__(self):
+            cache = {}
+            self.not_in_cache = not_in_cache = object()
+
+            def get(self, key):
+                return cache.get(key, not_in_cache)
+
+            def set(self, key, value):
+                cache[key] = value
+
+            def clear(self):
+                cache.clear()
+
+            def cache_len(self):
+                return len(cache)
+
+            self.get = types.MethodType(get, self)
+            self.set = types.MethodType(set, self)
+            self.clear = types.MethodType(clear, self)
+            self.__len__ = types.MethodType(cache_len, self)
+
+    if _OrderedDict is not None:
+        class _FifoCache(object):
+            def __init__(self, size):
+                self.not_in_cache = not_in_cache = object()
+
+                cache = _OrderedDict()
+
+                def get(self, key):
+                    return cache.get(key, not_in_cache)
+
+                def set(self, key, value):
+                    cache[key] = value
+                    while len(cache) > size:
+                        try:
+                            cache.popitem(False)
+                        except KeyError:
+                            pass
+
+                def clear(self):
+                    cache.clear()
+
+                def cache_len(self):
+                    return len(cache)
+
+                self.get = types.MethodType(get, self)
+                self.set = types.MethodType(set, self)
+                self.clear = types.MethodType(clear, self)
+                self.__len__ = types.MethodType(cache_len, self)
+
+    else:
+        class _FifoCache(object):
+            def __init__(self, size):
+                self.not_in_cache = not_in_cache = object()
+
+                cache = {}
+                key_fifo = collections.deque([], size)
+
+                def get(self, key):
+                    return cache.get(key, not_in_cache)
+
+                def set(self, key, value):
+                    cache[key] = value
+                    while len(key_fifo) > size:
+                        cache.pop(key_fifo.popleft(), None)
+                    key_fifo.append(key)
+
+                def clear(self):
+                    cache.clear()
+                    key_fifo.clear()
+
+                def cache_len(self):
+                    return len(cache)
+
+                self.get = types.MethodType(get, self)
+                self.set = types.MethodType(set, self)
+                self.clear = types.MethodType(clear, self)
+                self.__len__ = types.MethodType(cache_len, self)
+
+    # argument cache for optimizing repeated calls when backtracking through recursive expressions
+    packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail
+    packrat_cache_lock = RLock()
+    packrat_cache_stats = [0, 0]
+
+    # this method gets repeatedly called during backtracking with the same arguments -
+    # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
+    def _parseCache( self, instring, loc, doActions=True, callPreParse=True ):
+        HIT, MISS = 0, 1
+        lookup = (self, instring, loc, callPreParse, doActions)
+        with ParserElement.packrat_cache_lock:
+            cache = ParserElement.packrat_cache
+            value = cache.get(lookup)
+            if value is cache.not_in_cache:
+                ParserElement.packrat_cache_stats[MISS] += 1
+                try:
+                    value = self._parseNoCache(instring, loc, doActions, callPreParse)
+                except ParseBaseException as pe:
+                    # cache a copy of the exception, without the traceback
+                    cache.set(lookup, pe.__class__(*pe.args))
+                    raise
+                else:
+                    cache.set(lookup, (value[0], value[1].copy()))
+                    return value
+            else:
+                ParserElement.packrat_cache_stats[HIT] += 1
+                if isinstance(value, Exception):
+                    raise value
+                return (value[0], value[1].copy())
+
+    _parse = _parseNoCache
+
+    @staticmethod
+    def resetCache():
+        ParserElement.packrat_cache.clear()
+        ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats)
+
+    _packratEnabled = False
+    @staticmethod
+    def enablePackrat(cache_size_limit=128):
+        """Enables "packrat" parsing, which adds memoizing to the parsing logic.
+           Repeated parse attempts at the same string location (which happens
+           often in many complex grammars) can immediately return a cached value,
+           instead of re-executing parsing/validating code.  Memoizing is done of
+           both valid results and parsing exceptions.
+
+           Parameters:
+            - cache_size_limit - (default=C{128}) - if an integer value is provided
+              will limit the size of the packrat cache; if None is passed, then
+              the cache size will be unbounded; if 0 is passed, the cache will
+              be effectively disabled.
+
+           This speedup may break existing programs that use parse actions that
+           have side-effects.  For this reason, packrat parsing is disabled when
+           you first import pyparsing.  To activate the packrat feature, your
+           program must call the class method C{ParserElement.enablePackrat()}.  If
+           your program uses C{psyco} to "compile as you go", you must call
+           C{enablePackrat} before calling C{psyco.full()}.  If you do not do this,
+           Python will crash.  For best results, call C{enablePackrat()} immediately
+           after importing pyparsing.
+
+           Example::
+               import pyparsing
+               pyparsing.ParserElement.enablePackrat()
+        """
+        if not ParserElement._packratEnabled:
+            ParserElement._packratEnabled = True
+            if cache_size_limit is None:
+                ParserElement.packrat_cache = ParserElement._UnboundedCache()
+            else:
+                ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit)
+            ParserElement._parse = ParserElement._parseCache
+
+    def parseString( self, instring, parseAll=False ):
+        """
+        Execute the parse expression with the given string.
+        This is the main interface to the client code, once the complete
+        expression has been built.
+
+        If you want the grammar to require that the entire input string be
+        successfully parsed, then set C{parseAll} to True (equivalent to ending
+        the grammar with C{L{StringEnd()}}).
+
+        Note: C{parseString} implicitly calls C{expandtabs()} on the input string,
+        in order to report proper column numbers in parse actions.
+        If the input string contains tabs and
+        the grammar uses parse actions that use the C{loc} argument to index into the
+        string being parsed, you can ensure you have a consistent view of the input
+        string by:
+         - calling C{parseWithTabs} on your grammar before calling C{parseString}
+           (see L{I{parseWithTabs}<parseWithTabs>})
+         - define your parse action using the full C{(s,loc,toks)} signature, and
+           reference the input string using the parse action's C{s} argument
+         - explictly expand the tabs in your input string before calling
+           C{parseString}
+
+        Example::
+            Word('a').parseString('aaaaabaaa')  # -> ['aaaaa']
+            Word('a').parseString('aaaaabaaa', parseAll=True)  # -> Exception: Expected end of text
+        """
+        ParserElement.resetCache()
+        if not self.streamlined:
+            self.streamline()
+            #~ self.saveAsList = True
+        for e in self.ignoreExprs:
+            e.streamline()
+        if not self.keepTabs:
+            instring = instring.expandtabs()
+        try:
+            loc, tokens = self._parse( instring, 0 )
+            if parseAll:
+                loc = self.preParse( instring, loc )
+                se = Empty() + StringEnd()
+                se._parse( instring, loc )
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+        else:
+            return tokens
+
+    def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ):
+        """
+        Scan the input string for expression matches.  Each match will return the
+        matching tokens, start location, and end location.  May be called with optional
+        C{maxMatches} argument, to clip scanning after 'n' matches are found.  If
+        C{overlap} is specified, then overlapping matches will be reported.
+
+        Note that the start and end locations are reported relative to the string
+        being parsed.  See L{I{parseString}<parseString>} for more information on parsing
+        strings with embedded tabs.
+
+        Example::
+            source = "sldjf123lsdjjkf345sldkjf879lkjsfd987"
+            print(source)
+            for tokens,start,end in Word(alphas).scanString(source):
+                print(' '*start + '^'*(end-start))
+                print(' '*start + tokens[0])
+
+        prints::
+
+            sldjf123lsdjjkf345sldkjf879lkjsfd987
+            ^^^^^
+            sldjf
+                    ^^^^^^^
+                    lsdjjkf
+                              ^^^^^^
+                              sldkjf
+                                       ^^^^^^
+                                       lkjsfd
+        """
+        if not self.streamlined:
+            self.streamline()
+        for e in self.ignoreExprs:
+            e.streamline()
+
+        if not self.keepTabs:
+            instring = _ustr(instring).expandtabs()
+        instrlen = len(instring)
+        loc = 0
+        preparseFn = self.preParse
+        parseFn = self._parse
+        ParserElement.resetCache()
+        matches = 0
+        try:
+            while loc <= instrlen and matches < maxMatches:
+                try:
+                    preloc = preparseFn( instring, loc )
+                    nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
+                except ParseException:
+                    loc = preloc+1
+                else:
+                    if nextLoc > loc:
+                        matches += 1
+                        yield tokens, preloc, nextLoc
+                        if overlap:
+                            nextloc = preparseFn( instring, loc )
+                            if nextloc > loc:
+                                loc = nextLoc
+                            else:
+                                loc += 1
+                        else:
+                            loc = nextLoc
+                    else:
+                        loc = preloc+1
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def transformString( self, instring ):
+        """
+        Extension to C{L{scanString}}, to modify matching text with modified tokens that may
+        be returned from a parse action.  To use C{transformString}, define a grammar and
+        attach a parse action to it that modifies the returned token list.
+        Invoking C{transformString()} on a target string will then scan for matches,
+        and replace the matched text patterns according to the logic in the parse
+        action.  C{transformString()} returns the resulting transformed string.
+
+        Example::
+            wd = Word(alphas)
+            wd.setParseAction(lambda toks: toks[0].title())
+
+            print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york."))
+        Prints::
+            Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York.
+        """
+        out = []
+        lastE = 0
+        # force preservation of <TAB>s, to minimize unwanted transformation of string, and to
+        # keep string locs straight between transformString and scanString
+        self.keepTabs = True
+        try:
+            for t,s,e in self.scanString( instring ):
+                out.append( instring[lastE:s] )
+                if t:
+                    if isinstance(t,ParseResults):
+                        out += t.asList()
+                    elif isinstance(t,list):
+                        out += t
+                    else:
+                        out.append(t)
+                lastE = e
+            out.append(instring[lastE:])
+            out = [o for o in out if o]
+            return "".join(map(_ustr,_flatten(out)))
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def searchString( self, instring, maxMatches=_MAX_INT ):
+        """
+        Another extension to C{L{scanString}}, simplifying the access to the tokens found
+        to match the given parse expression.  May be called with optional
+        C{maxMatches} argument, to clip searching after 'n' matches are found.
+
+        Example::
+            # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters
+            cap_word = Word(alphas.upper(), alphas.lower())
+
+            print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))
+
+            # the sum() builtin can be used to merge results into a single ParseResults object
+            print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")))
+        prints::
+            [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
+            ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
+        """
+        try:
+            return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False):
+        """
+        Generator method to split a string using the given expression as a separator.
+        May be called with optional C{maxsplit} argument, to limit the number of splits;
+        and the optional C{includeSeparators} argument (default=C{False}), if the separating
+        matching text should be included in the split results.
+
+        Example::
+            punc = oneOf(list(".,;:/-!?"))
+            print(list(punc.split("This, this?, this sentence, is badly punctuated!")))
+        prints::
+            ['This', ' this', '', ' this sentence', ' is badly punctuated', '']
+        """
+        splits = 0
+        last = 0
+        for t,s,e in self.scanString(instring, maxMatches=maxsplit):
+            yield instring[last:s]
+            if includeSeparators:
+                yield t[0]
+            last = e
+        yield instring[last:]
+
+    def __add__(self, other ):
+        """
+        Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement
+        converts them to L{Literal}s by default.
+
+        Example::
+            greet = Word(alphas) + "," + Word(alphas) + "!"
+            hello = "Hello, World!"
+            print (hello, "->", greet.parseString(hello))
+        Prints::
+            Hello, World! -> ['Hello', ',', 'World', '!']
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return And( [ self, other ] )
+
+    def __radd__(self, other ):
+        """
+        Implementation of + operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other + self
+
+    def __sub__(self, other):
+        """
+        Implementation of - operator, returns C{L{And}} with error stop
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return self + And._ErrorStop() + other
+
+    def __rsub__(self, other ):
+        """
+        Implementation of - operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other - self
+
+    def __mul__(self,other):
+        """
+        Implementation of * operator, allows use of C{expr * 3} in place of
+        C{expr + expr + expr}.  Expressions may also me multiplied by a 2-integer
+        tuple, similar to C{{min,max}} multipliers in regular expressions.  Tuples
+        may also include C{None} as in:
+         - C{expr*(n,None)} or C{expr*(n,)} is equivalent
+              to C{expr*n + L{ZeroOrMore}(expr)}
+              (read as "at least n instances of C{expr}")
+         - C{expr*(None,n)} is equivalent to C{expr*(0,n)}
+              (read as "0 to n instances of C{expr}")
+         - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)}
+         - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)}
+
+        Note that C{expr*(None,n)} does not raise an exception if
+        more than n exprs exist in the input stream; that is,
+        C{expr*(None,n)} does not enforce a maximum number of expr
+        occurrences.  If this behavior is desired, then write
+        C{expr*(None,n) + ~expr}
+        """
+        if isinstance(other,int):
+            minElements, optElements = other,0
+        elif isinstance(other,tuple):
+            other = (other + (None, None))[:2]
+            if other[0] is None:
+                other = (0, other[1])
+            if isinstance(other[0],int) and other[1] is None:
+                if other[0] == 0:
+                    return ZeroOrMore(self)
+                if other[0] == 1:
+                    return OneOrMore(self)
+                else:
+                    return self*other[0] + ZeroOrMore(self)
+            elif isinstance(other[0],int) and isinstance(other[1],int):
+                minElements, optElements = other
+                optElements -= minElements
+            else:
+                raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1]))
+        else:
+            raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
+
+        if minElements < 0:
+            raise ValueError("cannot multiply ParserElement by negative value")
+        if optElements < 0:
+            raise ValueError("second tuple value must be greater or equal to first tuple value")
+        if minElements == optElements == 0:
+            raise ValueError("cannot multiply ParserElement by 0 or (0,0)")
+
+        if (optElements):
+            def makeOptionalList(n):
+                if n>1:
+                    return Optional(self + makeOptionalList(n-1))
+                else:
+                    return Optional(self)
+            if minElements:
+                if minElements == 1:
+                    ret = self + makeOptionalList(optElements)
+                else:
+                    ret = And([self]*minElements) + makeOptionalList(optElements)
+            else:
+                ret = makeOptionalList(optElements)
+        else:
+            if minElements == 1:
+                ret = self
+            else:
+                ret = And([self]*minElements)
+        return ret
+
+    def __rmul__(self, other):
+        return self.__mul__(other)
+
+    def __or__(self, other ):
+        """
+        Implementation of | operator - returns C{L{MatchFirst}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return MatchFirst( [ self, other ] )
+
+    def __ror__(self, other ):
+        """
+        Implementation of | operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other | self
+
+    def __xor__(self, other ):
+        """
+        Implementation of ^ operator - returns C{L{Or}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return Or( [ self, other ] )
+
+    def __rxor__(self, other ):
+        """
+        Implementation of ^ operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other ^ self
+
+    def __and__(self, other ):
+        """
+        Implementation of & operator - returns C{L{Each}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return Each( [ self, other ] )
+
+    def __rand__(self, other ):
+        """
+        Implementation of & operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other & self
+
+    def __invert__( self ):
+        """
+        Implementation of ~ operator - returns C{L{NotAny}}
+        """
+        return NotAny( self )
+
+    def __call__(self, name=None):
+        """
+        Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}.
+
+        If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be
+        passed as C{True}.
+
+        If C{name} is omitted, same as calling C{L{copy}}.
+
+        Example::
+            # these are equivalent
+            userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno")
+            userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")
+        """
+        if name is not None:
+            return self.setResultsName(name)
+        else:
+            return self.copy()
+
+    def suppress( self ):
+        """
+        Suppresses the output of this C{ParserElement}; useful to keep punctuation from
+        cluttering up returned output.
+        """
+        return Suppress( self )
+
+    def leaveWhitespace( self ):
+        """
+        Disables the skipping of whitespace before matching the characters in the
+        C{ParserElement}'s defined pattern.  This is normally only used internally by
+        the pyparsing module, but may be needed in some whitespace-sensitive grammars.
+        """
+        self.skipWhitespace = False
+        return self
+
+    def setWhitespaceChars( self, chars ):
+        """
+        Overrides the default whitespace chars
+        """
+        self.skipWhitespace = True
+        self.whiteChars = chars
+        self.copyDefaultWhiteChars = False
+        return self
+
+    def parseWithTabs( self ):
+        """
+        Overrides default behavior to expand C{<TAB>}s to spaces before parsing the input string.
+        Must be called before C{parseString} when the input grammar contains elements that
+        match C{<TAB>} characters.
+        """
+        self.keepTabs = True
+        return self
+
+    def ignore( self, other ):
+        """
+        Define expression to be ignored (e.g., comments) while doing pattern
+        matching; may be called repeatedly, to define multiple comment or other
+        ignorable patterns.
+
+        Example::
+            patt = OneOrMore(Word(alphas))
+            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj']
+
+            patt.ignore(cStyleComment)
+            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd']
+        """
+        if isinstance(other, basestring):
+            other = Suppress(other)
+
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                self.ignoreExprs.append(other)
+        else:
+            self.ignoreExprs.append( Suppress( other.copy() ) )
+        return self
+
+    def setDebugActions( self, startAction, successAction, exceptionAction ):
+        """
+        Enable display of debugging messages while doing pattern matching.
+        """
+        self.debugActions = (startAction or _defaultStartDebugAction,
+                             successAction or _defaultSuccessDebugAction,
+                             exceptionAction or _defaultExceptionDebugAction)
+        self.debug = True
+        return self
+
+    def setDebug( self, flag=True ):
+        """
+        Enable display of debugging messages while doing pattern matching.
+        Set C{flag} to True to enable, False to disable.
+
+        Example::
+            wd = Word(alphas).setName("alphaword")
+            integer = Word(nums).setName("numword")
+            term = wd | integer
+
+            # turn on debugging for wd
+            wd.setDebug()
+
+            OneOrMore(term).parseString("abc 123 xyz 890")
+
+        prints::
+            Match alphaword at loc 0(1,1)
+            Matched alphaword -> ['abc']
+            Match alphaword at loc 3(1,4)
+            Exception raised:Expected alphaword (at char 4), (line:1, col:5)
+            Match alphaword at loc 7(1,8)
+            Matched alphaword -> ['xyz']
+            Match alphaword at loc 11(1,12)
+            Exception raised:Expected alphaword (at char 12), (line:1, col:13)
+            Match alphaword at loc 15(1,16)
+            Exception raised:Expected alphaword (at char 15), (line:1, col:16)
+
+        The output shown is that produced by the default debug actions - custom debug actions can be
+        specified using L{setDebugActions}. Prior to attempting
+        to match the C{wd} expression, the debugging message C{"Match <exprname> at loc <n>(<line>,<col>)"}
+        is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"}
+        message is shown. Also note the use of L{setName} to assign a human-readable name to the expression,
+        which makes debugging and exception messages easier to understand - for instance, the default
+        name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}.
+        """
+        if flag:
+            self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction )
+        else:
+            self.debug = False
+        return self
+
+    def __str__( self ):
+        return self.name
+
+    def __repr__( self ):
+        return _ustr(self)
+
+    def streamline( self ):
+        self.streamlined = True
+        self.strRepr = None
+        return self
+
+    def checkRecursion( self, parseElementList ):
+        pass
+
+    def validate( self, validateTrace=[] ):
+        """
+        Check defined expressions for valid structure, check for infinite recursive definitions.
+        """
+        self.checkRecursion( [] )
+
+    def parseFile( self, file_or_filename, parseAll=False ):
+        """
+        Execute the parse expression on the given file or filename.
+        If a filename is specified (instead of a file object),
+        the entire file is opened, read, and closed before parsing.
+        """
+        try:
+            file_contents = file_or_filename.read()
+        except AttributeError:
+            with open(file_or_filename, "r") as f:
+                file_contents = f.read()
+        try:
+            return self.parseString(file_contents, parseAll)
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def __eq__(self,other):
+        if isinstance(other, ParserElement):
+            return self is other or vars(self) == vars(other)
+        elif isinstance(other, basestring):
+            return self.matches(other)
+        else:
+            return super(ParserElement,self)==other
+
+    def __ne__(self,other):
+        return not (self == other)
+
+    def __hash__(self):
+        return hash(id(self))
+
+    def __req__(self,other):
+        return self == other
+
+    def __rne__(self,other):
+        return not (self == other)
+
+    def matches(self, testString, parseAll=True):
+        """
+        Method for quick testing of a parser against a test string. Good for simple
+        inline microtests of sub expressions while building up larger parser.
+
+        Parameters:
+         - testString - to test against this expression for a match
+         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests
+
+        Example::
+            expr = Word(nums)
+            assert expr.matches("100")
+        """
+        try:
+            self.parseString(_ustr(testString), parseAll=parseAll)
+            return True
+        except ParseBaseException:
+            return False
+
+    def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False):
+        """
+        Execute the parse expression on a series of test strings, showing each
+        test, the parsed results or where the parse failed. Quick and easy way to
+        run a parse expression against a list of sample strings.
+
+        Parameters:
+         - tests - a list of separate test strings, or a multiline string of test strings
+         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests
+         - comment - (default=C{'#'}) - expression for indicating embedded comments in the test
+              string; pass None to disable comment filtering
+         - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline;
+              if False, only dump nested list
+         - printResults - (default=C{True}) prints test output to stdout
+         - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing
+
+        Returns: a (success, results) tuple, where success indicates that all tests succeeded
+        (or failed if C{failureTests} is True), and the results contain a list of lines of each
+        test's output
+
+        Example::
+            number_expr = pyparsing_common.number.copy()
+
+            result = number_expr.runTests('''
+                # unsigned integer
+                100
+                # negative integer
+                -100
+                # float with scientific notation
+                6.02e23
+                # integer with scientific notation
+                1e-12
+                ''')
+            print("Success" if result[0] else "Failed!")
+
+            result = number_expr.runTests('''
+                # stray character
+                100Z
+                # missing leading digit before '.'
+                -.100
+                # too many '.'
+                3.14.159
+                ''', failureTests=True)
+            print("Success" if result[0] else "Failed!")
+        prints::
+            # unsigned integer
+            100
+            [100]
+
+            # negative integer
+            -100
+            [-100]
+
+            # float with scientific notation
+            6.02e23
+            [6.02e+23]
+
+            # integer with scientific notation
+            1e-12
+            [1e-12]
+
+            Success
+
+            # stray character
+            100Z
+               ^
+            FAIL: Expected end of text (at char 3), (line:1, col:4)
+
+            # missing leading digit before '.'
+            -.100
+            ^
+            FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)
+
+            # too many '.'
+            3.14.159
+                ^
+            FAIL: Expected end of text (at char 4), (line:1, col:5)
+
+            Success
+
+        Each test string must be on a single line. If you want to test a string that spans multiple
+        lines, create a test like this::
+
+            expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")
+
+        (Note that this is a raw string literal, you must include the leading 'r'.)
+        """
+        if isinstance(tests, basestring):
+            tests = list(map(str.strip, tests.rstrip().splitlines()))
+        if isinstance(comment, basestring):
+            comment = Literal(comment)
+        allResults = []
+        comments = []
+        success = True
+        for t in tests:
+            if comment is not None and comment.matches(t, False) or comments and not t:
+                comments.append(t)
+                continue
+            if not t:
+                continue
+            out = ['\n'.join(comments), t]
+            comments = []
+            try:
+                t = t.replace(r'\n','\n')
+                result = self.parseString(t, parseAll=parseAll)
+                out.append(result.dump(full=fullDump))
+                success = success and not failureTests
+            except ParseBaseException as pe:
+                fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else ""
+                if '\n' in t:
+                    out.append(line(pe.loc, t))
+                    out.append(' '*(col(pe.loc,t)-1) + '^' + fatal)
+                else:
+                    out.append(' '*pe.loc + '^' + fatal)
+                out.append("FAIL: " + str(pe))
+                success = success and failureTests
+                result = pe
+            except Exception as exc:
+                out.append("FAIL-EXCEPTION: " + str(exc))
+                success = success and failureTests
+                result = exc
+
+            if printResults:
+                if fullDump:
+                    out.append('')
+                print('\n'.join(out))
+
+            allResults.append((t, result))
+
+        return success, allResults
+
+
+class Token(ParserElement):
+    """
+    Abstract C{ParserElement} subclass, for defining atomic matching patterns.
+    """
+    def __init__( self ):
+        super(Token,self).__init__( savelist=False )
+
+
+class Empty(Token):
+    """
+    An empty token, will always match.
+    """
+    def __init__( self ):
+        super(Empty,self).__init__()
+        self.name = "Empty"
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+
+
+class NoMatch(Token):
+    """
+    A token that will never match.
+    """
+    def __init__( self ):
+        super(NoMatch,self).__init__()
+        self.name = "NoMatch"
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+        self.errmsg = "Unmatchable token"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        raise ParseException(instring, loc, self.errmsg, self)
+
+
+class Literal(Token):
+    """
+    Token to exactly match a specified string.
+
+    Example::
+        Literal('blah').parseString('blah')  # -> ['blah']
+        Literal('blah').parseString('blahfooblah')  # -> ['blah']
+        Literal('blah').parseString('bla')  # -> Exception: Expected "blah"
+
+    For case-insensitive matching, use L{CaselessLiteral}.
+
+    For keyword matching (force word break before and after the matched string),
+    use L{Keyword} or L{CaselessKeyword}.
+    """
+    def __init__( self, matchString ):
+        super(Literal,self).__init__()
+        self.match = matchString
+        self.matchLen = len(matchString)
+        try:
+            self.firstMatchChar = matchString[0]
+        except IndexError:
+            warnings.warn("null string passed to Literal; use Empty() instead",
+                            SyntaxWarning, stacklevel=2)
+            self.__class__ = Empty
+        self.name = '"%s"' % _ustr(self.match)
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = False
+        self.mayIndexError = False
+
+    # Performance tuning: this routine gets called a *lot*
+    # if this is a single character match string  and the first character matches,
+    # short-circuit as quickly as possible, and avoid calling startswith
+    #~ @profile
+    def parseImpl( self, instring, loc, doActions=True ):
+        if (instring[loc] == self.firstMatchChar and
+            (self.matchLen==1 or instring.startswith(self.match,loc)) ):
+            return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+_L = Literal
+ParserElement._literalStringClass = Literal
+
+class Keyword(Token):
+    """
+    Token to exactly match a specified string as a keyword, that is, it must be
+    immediately followed by a non-keyword character.  Compare with C{L{Literal}}:
+     - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}.
+     - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'}
+    Accepts two optional constructor arguments in addition to the keyword string:
+     - C{identChars} is a string of characters that would be valid identifier characters,
+          defaulting to all alphanumerics + "_" and "$"
+     - C{caseless} allows case-insensitive matching, default is C{False}.
+
+    Example::
+        Keyword("start").parseString("start")  # -> ['start']
+        Keyword("start").parseString("starting")  # -> Exception
+
+    For case-insensitive matching, use L{CaselessKeyword}.
+    """
+    DEFAULT_KEYWORD_CHARS = alphanums+"_$"
+
+    def __init__( self, matchString, identChars=None, caseless=False ):
+        super(Keyword,self).__init__()
+        if identChars is None:
+            identChars = Keyword.DEFAULT_KEYWORD_CHARS
+        self.match = matchString
+        self.matchLen = len(matchString)
+        try:
+            self.firstMatchChar = matchString[0]
+        except IndexError:
+            warnings.warn("null string passed to Keyword; use Empty() instead",
+                            SyntaxWarning, stacklevel=2)
+        self.name = '"%s"' % self.match
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = False
+        self.mayIndexError = False
+        self.caseless = caseless
+        if caseless:
+            self.caselessmatch = matchString.upper()
+            identChars = identChars.upper()
+        self.identChars = set(identChars)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.caseless:
+            if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
+                 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and
+                 (loc == 0 or instring[loc-1].upper() not in self.identChars) ):
+                return loc+self.matchLen, self.match
+        else:
+            if (instring[loc] == self.firstMatchChar and
+                (self.matchLen==1 or instring.startswith(self.match,loc)) and
+                (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and
+                (loc == 0 or instring[loc-1] not in self.identChars) ):
+                return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+    def copy(self):
+        c = super(Keyword,self).copy()
+        c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
+        return c
+
+    @staticmethod
+    def setDefaultKeywordChars( chars ):
+        """Overrides the default Keyword chars
+        """
+        Keyword.DEFAULT_KEYWORD_CHARS = chars
+
+class CaselessLiteral(Literal):
+    """
+    Token to match a specified string, ignoring case of letters.
+    Note: the matched results will always be in the case of the given
+    match string, NOT the case of the input text.
+
+    Example::
+        OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD']
+
+    (Contrast with example for L{CaselessKeyword}.)
+    """
+    def __init__( self, matchString ):
+        super(CaselessLiteral,self).__init__( matchString.upper() )
+        # Preserve the defining literal.
+        self.returnString = matchString
+        self.name = "'%s'" % self.returnString
+        self.errmsg = "Expected " + self.name
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if instring[ loc:loc+self.matchLen ].upper() == self.match:
+            return loc+self.matchLen, self.returnString
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class CaselessKeyword(Keyword):
+    """
+    Caseless version of L{Keyword}.
+
+    Example::
+        OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD']
+
+    (Contrast with example for L{CaselessLiteral}.)
+    """
+    def __init__( self, matchString, identChars=None ):
+        super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True )
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
+             (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ):
+            return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class CloseMatch(Token):
+    """
+    A variation on L{Literal} which matches "close" matches, that is,
+    strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters:
+     - C{match_string} - string to be matched
+     - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match
+
+    The results from a successful parse will contain the matched text from the input string and the following named results:
+     - C{mismatches} - a list of the positions within the match_string where mismatches were found
+     - C{original} - the original match_string used to compare against the input string
+
+    If C{mismatches} is an empty list, then the match was an exact match.
+
+    Example::
+        patt = CloseMatch("ATCATCGAATGGA")
+        patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']})
+        patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1)
+
+        # exact match
+        patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']})
+
+        # close match allowing up to 2 mismatches
+        patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2)
+        patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']})
+    """
+    def __init__(self, match_string, maxMismatches=1):
+        super(CloseMatch,self).__init__()
+        self.name = match_string
+        self.match_string = match_string
+        self.maxMismatches = maxMismatches
+        self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches)
+        self.mayIndexError = False
+        self.mayReturnEmpty = False
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        start = loc
+        instrlen = len(instring)
+        maxloc = start + len(self.match_string)
+
+        if maxloc <= instrlen:
+            match_string = self.match_string
+            match_stringloc = 0
+            mismatches = []
+            maxMismatches = self.maxMismatches
+
+            for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)):
+                src,mat = s_m
+                if src != mat:
+                    mismatches.append(match_stringloc)
+                    if len(mismatches) > maxMismatches:
+                        break
+            else:
+                loc = match_stringloc + 1
+                results = ParseResults([instring[start:loc]])
+                results['original'] = self.match_string
+                results['mismatches'] = mismatches
+                return loc, results
+
+        raise ParseException(instring, loc, self.errmsg, self)
+
+
+class Word(Token):
+    """
+    Token for matching words composed of allowed character sets.
+    Defined with string containing all allowed initial characters,
+    an optional string containing allowed body characters (if omitted,
+    defaults to the initial character set), and an optional minimum,
+    maximum, and/or exact length.  The default value for C{min} is 1 (a
+    minimum value < 1 is not valid); the default values for C{max} and C{exact}
+    are 0, meaning no maximum or exact length restriction. An optional
+    C{excludeChars} parameter can list characters that might be found in
+    the input C{bodyChars} string; useful to define a word of all printables
+    except for one or two characters, for instance.
+
+    L{srange} is useful for defining custom character set strings for defining
+    C{Word} expressions, using range notation from regular expression character sets.
+
+    A common mistake is to use C{Word} to match a specific literal string, as in
+    C{Word("Address")}. Remember that C{Word} uses the string argument to define
+    I{sets} of matchable characters. This expression would match "Add", "AAA",
+    "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'.
+    To match an exact literal string, use L{Literal} or L{Keyword}.
+
+    pyparsing includes helper strings for building Words:
+     - L{alphas}
+     - L{nums}
+     - L{alphanums}
+     - L{hexnums}
+     - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.)
+     - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.)
+     - L{printables} (any non-whitespace character)
+
+    Example::
+        # a word composed of digits
+        integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9"))
+
+        # a word with a leading capital, and zero or more lowercase
+        capital_word = Word(alphas.upper(), alphas.lower())
+
+        # hostnames are alphanumeric, with leading alpha, and '-'
+        hostname = Word(alphas, alphanums+'-')
+
+        # roman numeral (not a strict parser, accepts invalid mix of characters)
+        roman = Word("IVXLCDM")
+
+        # any string of non-whitespace characters, except for ','
+        csv_value = Word(printables, excludeChars=",")
+    """
+    def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ):
+        super(Word,self).__init__()
+        if excludeChars:
+            initChars = ''.join(c for c in initChars if c not in excludeChars)
+            if bodyChars:
+                bodyChars = ''.join(c for c in bodyChars if c not in excludeChars)
+        self.initCharsOrig = initChars
+        self.initChars = set(initChars)
+        if bodyChars :
+            self.bodyCharsOrig = bodyChars
+            self.bodyChars = set(bodyChars)
+        else:
+            self.bodyCharsOrig = initChars
+            self.bodyChars = set(initChars)
+
+        self.maxSpecified = max > 0
+
+        if min < 1:
+            raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.asKeyword = asKeyword
+
+        if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0):
+            if self.bodyCharsOrig == self.initCharsOrig:
+                self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
+            elif len(self.initCharsOrig) == 1:
+                self.reString = "%s[%s]*" % \
+                                      (re.escape(self.initCharsOrig),
+                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
+            else:
+                self.reString = "[%s][%s]*" % \
+                                      (_escapeRegexRangeChars(self.initCharsOrig),
+                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
+            if self.asKeyword:
+                self.reString = r"\b"+self.reString+r"\b"
+            try:
+                self.re = re.compile( self.reString )
+            except Exception:
+                self.re = None
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.re:
+            result = self.re.match(instring,loc)
+            if not result:
+                raise ParseException(instring, loc, self.errmsg, self)
+
+            loc = result.end()
+            return loc, result.group()
+
+        if not(instring[ loc ] in self.initChars):
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        start = loc
+        loc += 1
+        instrlen = len(instring)
+        bodychars = self.bodyChars
+        maxloc = start + self.maxLen
+        maxloc = min( maxloc, instrlen )
+        while loc < maxloc and instring[loc] in bodychars:
+            loc += 1
+
+        throwException = False
+        if loc - start < self.minLen:
+            throwException = True
+        if self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
+            throwException = True
+        if self.asKeyword:
+            if (start>0 and instring[start-1] in bodychars) or (loc<instrlen and instring[loc] in bodychars):
+                throwException = True
+
+        if throwException:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+    def __str__( self ):
+        try:
+            return super(Word,self).__str__()
+        except Exception:
+            pass
+
+
+        if self.strRepr is None:
+
+            def charsAsStr(s):
+                if len(s)>4:
+                    return s[:4]+"..."
+                else:
+                    return s
+
+            if ( self.initCharsOrig != self.bodyCharsOrig ):
+                self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) )
+            else:
+                self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
+
+        return self.strRepr
+
+
+class Regex(Token):
+    r"""
+    Token for matching strings that match a given regular expression.
+    Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
+    If the given regex contains named groups (defined using C{(?P<name>...)}), these will be preserved as
+    named parse results.
+
+    Example::
+        realnum = Regex(r"[+-]?\d+\.\d*")
+        date = Regex(r'(?P<year>\d{4})-(?P<month>\d\d?)-(?P<day>\d\d?)')
+        # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
+        roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})")
+    """
+    compiledREtype = type(re.compile("[A-Z]"))
+    def __init__( self, pattern, flags=0):
+        """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags."""
+        super(Regex,self).__init__()
+
+        if isinstance(pattern, basestring):
+            if not pattern:
+                warnings.warn("null string passed to Regex; use Empty() instead",
+                        SyntaxWarning, stacklevel=2)
+
+            self.pattern = pattern
+            self.flags = flags
+
+            try:
+                self.re = re.compile(self.pattern, self.flags)
+                self.reString = self.pattern
+            except sre_constants.error:
+                warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
+                    SyntaxWarning, stacklevel=2)
+                raise
+
+        elif isinstance(pattern, Regex.compiledREtype):
+            self.re = pattern
+            self.pattern = \
+            self.reString = str(pattern)
+            self.flags = flags
+
+        else:
+            raise ValueError("Regex may only be constructed with a string or a compiled RE object")
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        result = self.re.match(instring,loc)
+        if not result:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        loc = result.end()
+        d = result.groupdict()
+        ret = ParseResults(result.group())
+        if d:
+            for k in d:
+                ret[k] = d[k]
+        return loc,ret
+
+    def __str__( self ):
+        try:
+            return super(Regex,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "Re:(%s)" % repr(self.pattern)
+
+        return self.strRepr
+
+
+class QuotedString(Token):
+    r"""
+    Token for matching strings that are delimited by quoting characters.
+
+    Defined with the following parameters:
+        - quoteChar - string of one or more characters defining the quote delimiting string
+        - escChar - character to escape quotes, typically backslash (default=C{None})
+        - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None})
+        - multiline - boolean indicating whether quotes can span multiple lines (default=C{False})
+        - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True})
+        - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar)
+        - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True})
+
+    Example::
+        qs = QuotedString('"')
+        print(qs.searchString('lsjdf "This is the quote" sldjf'))
+        complex_qs = QuotedString('{{', endQuoteChar='}}')
+        print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf'))
+        sql_qs = QuotedString('"', escQuote='""')
+        print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf'))
+    prints::
+        [['This is the quote']]
+        [['This is the "quote"']]
+        [['This is the quote with "embedded" quotes']]
+    """
+    def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True):
+        super(QuotedString,self).__init__()
+
+        # remove white space from quote chars - wont work anyway
+        quoteChar = quoteChar.strip()
+        if not quoteChar:
+            warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
+            raise SyntaxError()
+
+        if endQuoteChar is None:
+            endQuoteChar = quoteChar
+        else:
+            endQuoteChar = endQuoteChar.strip()
+            if not endQuoteChar:
+                warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
+                raise SyntaxError()
+
+        self.quoteChar = quoteChar
+        self.quoteCharLen = len(quoteChar)
+        self.firstQuoteChar = quoteChar[0]
+        self.endQuoteChar = endQuoteChar
+        self.endQuoteCharLen = len(endQuoteChar)
+        self.escChar = escChar
+        self.escQuote = escQuote
+        self.unquoteResults = unquoteResults
+        self.convertWhitespaceEscapes = convertWhitespaceEscapes
+
+        if multiline:
+            self.flags = re.MULTILINE | re.DOTALL
+            self.pattern = r'%s(?:[^%s%s]' % \
+                ( re.escape(self.quoteChar),
+                  _escapeRegexRangeChars(self.endQuoteChar[0]),
+                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
+        else:
+            self.flags = 0
+            self.pattern = r'%s(?:[^%s\n\r%s]' % \
+                ( re.escape(self.quoteChar),
+                  _escapeRegexRangeChars(self.endQuoteChar[0]),
+                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
+        if len(self.endQuoteChar) > 1:
+            self.pattern += (
+                '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]),
+                                               _escapeRegexRangeChars(self.endQuoteChar[i]))
+                                    for i in range(len(self.endQuoteChar)-1,0,-1)) + ')'
+                )
+        if escQuote:
+            self.pattern += (r'|(?:%s)' % re.escape(escQuote))
+        if escChar:
+            self.pattern += (r'|(?:%s.)' % re.escape(escChar))
+            self.escCharReplacePattern = re.escape(self.escChar)+"(.)"
+        self.pattern += (r')*%s' % re.escape(self.endQuoteChar))
+
+        try:
+            self.re = re.compile(self.pattern, self.flags)
+            self.reString = self.pattern
+        except sre_constants.error:
+            warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern,
+                SyntaxWarning, stacklevel=2)
+            raise
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
+        if not result:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        loc = result.end()
+        ret = result.group()
+
+        if self.unquoteResults:
+
+            # strip off quotes
+            ret = ret[self.quoteCharLen:-self.endQuoteCharLen]
+
+            if isinstance(ret,basestring):
+                # replace escaped whitespace
+                if '\\' in ret and self.convertWhitespaceEscapes:
+                    ws_map = {
+                        r'\t' : '\t',
+                        r'\n' : '\n',
+                        r'\f' : '\f',
+                        r'\r' : '\r',
+                    }
+                    for wslit,wschar in ws_map.items():
+                        ret = ret.replace(wslit, wschar)
+
+                # replace escaped characters
+                if self.escChar:
+                    ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret)
+
+                # replace escaped quotes
+                if self.escQuote:
+                    ret = ret.replace(self.escQuote, self.endQuoteChar)
+
+        return loc, ret
+
+    def __str__( self ):
+        try:
+            return super(QuotedString,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar)
+
+        return self.strRepr
+
+
+class CharsNotIn(Token):
+    """
+    Token for matching words composed of characters I{not} in a given set (will
+    include whitespace in matched characters if not listed in the provided exclusion set - see example).
+    Defined with string containing all disallowed characters, and an optional
+    minimum, maximum, and/or exact length.  The default value for C{min} is 1 (a
+    minimum value < 1 is not valid); the default values for C{max} and C{exact}
+    are 0, meaning no maximum or exact length restriction.
+
+    Example::
+        # define a comma-separated-value as anything that is not a ','
+        csv_value = CharsNotIn(',')
+        print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213"))
+    prints::
+        ['dkls', 'lsdkjf', 's12 34', '@!#', '213']
+    """
+    def __init__( self, notChars, min=1, max=0, exact=0 ):
+        super(CharsNotIn,self).__init__()
+        self.skipWhitespace = False
+        self.notChars = notChars
+
+        if min < 1:
+            raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted")
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = ( self.minLen == 0 )
+        self.mayIndexError = False
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if instring[loc] in self.notChars:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        start = loc
+        loc += 1
+        notchars = self.notChars
+        maxlen = min( start+self.maxLen, len(instring) )
+        while loc < maxlen and \
+              (instring[loc] not in notchars):
+            loc += 1
+
+        if loc - start < self.minLen:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+    def __str__( self ):
+        try:
+            return super(CharsNotIn, self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            if len(self.notChars) > 4:
+                self.strRepr = "!W:(%s...)" % self.notChars[:4]
+            else:
+                self.strRepr = "!W:(%s)" % self.notChars
+
+        return self.strRepr
+
+class White(Token):
+    """
+    Special matching class for matching whitespace.  Normally, whitespace is ignored
+    by pyparsing grammars.  This class is included when some whitespace structures
+    are significant.  Define with a string containing the whitespace characters to be
+    matched; default is C{" \\t\\r\\n"}.  Also takes optional C{min}, C{max}, and C{exact} arguments,
+    as defined for the C{L{Word}} class.
+    """
+    whiteStrs = {
+        " " : "<SPC>",
+        "\t": "<TAB>",
+        "\n": "<LF>",
+        "\r": "<CR>",
+        "\f": "<FF>",
+        }
+    def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
+        super(White,self).__init__()
+        self.matchWhite = ws
+        self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) )
+        #~ self.leaveWhitespace()
+        self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite))
+        self.mayReturnEmpty = True
+        self.errmsg = "Expected " + self.name
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if not(instring[ loc ] in self.matchWhite):
+            raise ParseException(instring, loc, self.errmsg, self)
+        start = loc
+        loc += 1
+        maxloc = start + self.maxLen
+        maxloc = min( maxloc, len(instring) )
+        while loc < maxloc and instring[loc] in self.matchWhite:
+            loc += 1
+
+        if loc - start < self.minLen:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+
+class _PositionToken(Token):
+    def __init__( self ):
+        super(_PositionToken,self).__init__()
+        self.name=self.__class__.__name__
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+
+class GoToColumn(_PositionToken):
+    """
+    Token to advance to a specific column of input text; useful for tabular report scraping.
+    """
+    def __init__( self, colno ):
+        super(GoToColumn,self).__init__()
+        self.col = colno
+
+    def preParse( self, instring, loc ):
+        if col(loc,instring) != self.col:
+            instrlen = len(instring)
+            if self.ignoreExprs:
+                loc = self._skipIgnorables( instring, loc )
+            while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col :
+                loc += 1
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        thiscol = col( loc, instring )
+        if thiscol > self.col:
+            raise ParseException( instring, loc, "Text not in expected column", self )
+        newloc = loc + self.col - thiscol
+        ret = instring[ loc: newloc ]
+        return newloc, ret
+
+
+class LineStart(_PositionToken):
+    """
+    Matches if current position is at the beginning of a line within the parse string
+
+    Example::
+
+        test = '''\
+        AAA this line
+        AAA and this line
+          AAA but not this one
+        B AAA and definitely not this one
+        '''
+
+        for t in (LineStart() + 'AAA' + restOfLine).searchString(test):
+            print(t)
+
+    Prints::
+        ['AAA', ' this line']
+        ['AAA', ' and this line']
+
+    """
+    def __init__( self ):
+        super(LineStart,self).__init__()
+        self.errmsg = "Expected start of line"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if col(loc, instring) == 1:
+            return loc, []
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class LineEnd(_PositionToken):
+    """
+    Matches if current position is at the end of a line within the parse string
+    """
+    def __init__( self ):
+        super(LineEnd,self).__init__()
+        self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
+        self.errmsg = "Expected end of line"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if loc<len(instring):
+            if instring[loc] == "\n":
+                return loc+1, "\n"
+            else:
+                raise ParseException(instring, loc, self.errmsg, self)
+        elif loc == len(instring):
+            return loc+1, []
+        else:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+class StringStart(_PositionToken):
+    """
+    Matches if current position is at the beginning of the parse string
+    """
+    def __init__( self ):
+        super(StringStart,self).__init__()
+        self.errmsg = "Expected start of text"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if loc != 0:
+            # see if entire string up to here is just whitespace and ignoreables
+            if loc != self.preParse( instring, 0 ):
+                raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+class StringEnd(_PositionToken):
+    """
+    Matches if current position is at the end of the parse string
+    """
+    def __init__( self ):
+        super(StringEnd,self).__init__()
+        self.errmsg = "Expected end of text"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if loc < len(instring):
+            raise ParseException(instring, loc, self.errmsg, self)
+        elif loc == len(instring):
+            return loc+1, []
+        elif loc > len(instring):
+            return loc, []
+        else:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+class WordStart(_PositionToken):
+    """
+    Matches if the current position is at the beginning of a Word, and
+    is not preceded by any character in a given set of C{wordChars}
+    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
+    use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of
+    the string being parsed, or at the beginning of a line.
+    """
+    def __init__(self, wordChars = printables):
+        super(WordStart,self).__init__()
+        self.wordChars = set(wordChars)
+        self.errmsg = "Not at the start of a word"
+
+    def parseImpl(self, instring, loc, doActions=True ):
+        if loc != 0:
+            if (instring[loc-1] in self.wordChars or
+                instring[loc] not in self.wordChars):
+                raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+class WordEnd(_PositionToken):
+    """
+    Matches if the current position is at the end of a Word, and
+    is not followed by any character in a given set of C{wordChars}
+    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
+    use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of
+    the string being parsed, or at the end of a line.
+    """
+    def __init__(self, wordChars = printables):
+        super(WordEnd,self).__init__()
+        self.wordChars = set(wordChars)
+        self.skipWhitespace = False
+        self.errmsg = "Not at the end of a word"
+
+    def parseImpl(self, instring, loc, doActions=True ):
+        instrlen = len(instring)
+        if instrlen>0 and loc<instrlen:
+            if (instring[loc] in self.wordChars or
+                instring[loc-1] not in self.wordChars):
+                raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+
+class ParseExpression(ParserElement):
+    """
+    Abstract subclass of ParserElement, for combining and post-processing parsed tokens.
+    """
+    def __init__( self, exprs, savelist = False ):
+        super(ParseExpression,self).__init__(savelist)
+        if isinstance( exprs, _generatorType ):
+            exprs = list(exprs)
+
+        if isinstance( exprs, basestring ):
+            self.exprs = [ ParserElement._literalStringClass( exprs ) ]
+        elif isinstance( exprs, collections.Iterable ):
+            exprs = list(exprs)
+            # if sequence of strings provided, wrap with Literal
+            if all(isinstance(expr, basestring) for expr in exprs):
+                exprs = map(ParserElement._literalStringClass, exprs)
+            self.exprs = list(exprs)
+        else:
+            try:
+                self.exprs = list( exprs )
+            except TypeError:
+                self.exprs = [ exprs ]
+        self.callPreparse = False
+
+    def __getitem__( self, i ):
+        return self.exprs[i]
+
+    def append( self, other ):
+        self.exprs.append( other )
+        self.strRepr = None
+        return self
+
+    def leaveWhitespace( self ):
+        """Extends C{leaveWhitespace} defined in base class, and also invokes C{leaveWhitespace} on
+           all contained expressions."""
+        self.skipWhitespace = False
+        self.exprs = [ e.copy() for e in self.exprs ]
+        for e in self.exprs:
+            e.leaveWhitespace()
+        return self
+
+    def ignore( self, other ):
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                super( ParseExpression, self).ignore( other )
+                for e in self.exprs:
+                    e.ignore( self.ignoreExprs[-1] )
+        else:
+            super( ParseExpression, self).ignore( other )
+            for e in self.exprs:
+                e.ignore( self.ignoreExprs[-1] )
+        return self
+
+    def __str__( self ):
+        try:
+            return super(ParseExpression,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.exprs) )
+        return self.strRepr
+
+    def streamline( self ):
+        super(ParseExpression,self).streamline()
+
+        for e in self.exprs:
+            e.streamline()
+
+        # collapse nested And's of the form And( And( And( a,b), c), d) to And( a,b,c,d )
+        # but only if there are no parse actions or resultsNames on the nested And's
+        # (likewise for Or's and MatchFirst's)
+        if ( len(self.exprs) == 2 ):
+            other = self.exprs[0]
+            if ( isinstance( other, self.__class__ ) and
+                  not(other.parseAction) and
+                  other.resultsName is None and
+                  not other.debug ):
+                self.exprs = other.exprs[:] + [ self.exprs[1] ]
+                self.strRepr = None
+                self.mayReturnEmpty |= other.mayReturnEmpty
+                self.mayIndexError  |= other.mayIndexError
+
+            other = self.exprs[-1]
+            if ( isinstance( other, self.__class__ ) and
+                  not(other.parseAction) and
+                  other.resultsName is None and
+                  not other.debug ):
+                self.exprs = self.exprs[:-1] + other.exprs[:]
+                self.strRepr = None
+                self.mayReturnEmpty |= other.mayReturnEmpty
+                self.mayIndexError  |= other.mayIndexError
+
+        self.errmsg = "Expected " + _ustr(self)
+
+        return self
+
+    def setResultsName( self, name, listAllMatches=False ):
+        ret = super(ParseExpression,self).setResultsName(name,listAllMatches)
+        return ret
+
+    def validate( self, validateTrace=[] ):
+        tmp = validateTrace[:]+[self]
+        for e in self.exprs:
+            e.validate(tmp)
+        self.checkRecursion( [] )
+
+    def copy(self):
+        ret = super(ParseExpression,self).copy()
+        ret.exprs = [e.copy() for e in self.exprs]
+        return ret
+
+class And(ParseExpression):
+    """
+    Requires all given C{ParseExpression}s to be found in the given order.
+    Expressions may be separated by whitespace.
+    May be constructed using the C{'+'} operator.
+    May also be constructed using the C{'-'} operator, which will suppress backtracking.
+
+    Example::
+        integer = Word(nums)
+        name_expr = OneOrMore(Word(alphas))
+
+        expr = And([integer("id"),name_expr("name"),integer("age")])
+        # more easily written as:
+        expr = integer("id") + name_expr("name") + integer("age")
+    """
+
+    class _ErrorStop(Empty):
+        def __init__(self, *args, **kwargs):
+            super(And._ErrorStop,self).__init__(*args, **kwargs)
+            self.name = '-'
+            self.leaveWhitespace()
+
+    def __init__( self, exprs, savelist = True ):
+        super(And,self).__init__(exprs, savelist)
+        self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
+        self.setWhitespaceChars( self.exprs[0].whiteChars )
+        self.skipWhitespace = self.exprs[0].skipWhitespace
+        self.callPreparse = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        # pass False as last arg to _parse for first element, since we already
+        # pre-parsed the string as part of our And pre-parsing
+        loc, resultlist = self.exprs[0]._parse( instring, loc, doActions, callPreParse=False )
+        errorStop = False
+        for e in self.exprs[1:]:
+            if isinstance(e, And._ErrorStop):
+                errorStop = True
+                continue
+            if errorStop:
+                try:
+                    loc, exprtokens = e._parse( instring, loc, doActions )
+                except ParseSyntaxException:
+                    raise
+                except ParseBaseException as pe:
+                    pe.__traceback__ = None
+                    raise ParseSyntaxException._from_exception(pe)
+                except IndexError:
+                    raise ParseSyntaxException(instring, len(instring), self.errmsg, self)
+            else:
+                loc, exprtokens = e._parse( instring, loc, doActions )
+            if exprtokens or exprtokens.haskeys():
+                resultlist += exprtokens
+        return loc, resultlist
+
+    def __iadd__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        return self.append( other ) #And( [ self, other ] )
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+            if not e.mayReturnEmpty:
+                break
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+
+class Or(ParseExpression):
+    """
+    Requires that at least one C{ParseExpression} is found.
+    If two expressions match, the expression that matches the longest string will be used.
+    May be constructed using the C{'^'} operator.
+
+    Example::
+        # construct Or using '^' operator
+
+        number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums))
+        print(number.searchString("123 3.1416 789"))
+    prints::
+        [['123'], ['3.1416'], ['789']]
+    """
+    def __init__( self, exprs, savelist = False ):
+        super(Or,self).__init__(exprs, savelist)
+        if self.exprs:
+            self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
+        else:
+            self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        maxExcLoc = -1
+        maxException = None
+        matches = []
+        for e in self.exprs:
+            try:
+                loc2 = e.tryParse( instring, loc )
+            except ParseException as err:
+                err.__traceback__ = None
+                if err.loc > maxExcLoc:
+                    maxException = err
+                    maxExcLoc = err.loc
+            except IndexError:
+                if len(instring) > maxExcLoc:
+                    maxException = ParseException(instring,len(instring),e.errmsg,self)
+                    maxExcLoc = len(instring)
+            else:
+                # save match among all matches, to retry longest to shortest
+                matches.append((loc2, e))
+
+        if matches:
+            matches.sort(key=lambda x: -x[0])
+            for _,e in matches:
+                try:
+                    return e._parse( instring, loc, doActions )
+                except ParseException as err:
+                    err.__traceback__ = None
+                    if err.loc > maxExcLoc:
+                        maxException = err
+                        maxExcLoc = err.loc
+
+        if maxException is not None:
+            maxException.msg = self.errmsg
+            raise maxException
+        else:
+            raise ParseException(instring, loc, "no defined alternatives to match", self)
+
+
+    def __ixor__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        return self.append( other ) #Or( [ self, other ] )
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class MatchFirst(ParseExpression):
+    """
+    Requires that at least one C{ParseExpression} is found.
+    If two expressions match, the first one listed is the one that will match.
+    May be constructed using the C{'|'} operator.
+
+    Example::
+        # construct MatchFirst using '|' operator
+
+        # watch the order of expressions to match
+        number = Word(nums) | Combine(Word(nums) + '.' + Word(nums))
+        print(number.searchString("123 3.1416 789")) #  Fail! -> [['123'], ['3'], ['1416'], ['789']]
+
+        # put more selective expression first
+        number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums)
+        print(number.searchString("123 3.1416 789")) #  Better -> [['123'], ['3.1416'], ['789']]
+    """
+    def __init__( self, exprs, savelist = False ):
+        super(MatchFirst,self).__init__(exprs, savelist)
+        if self.exprs:
+            self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
+        else:
+            self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        maxExcLoc = -1
+        maxException = None
+        for e in self.exprs:
+            try:
+                ret = e._parse( instring, loc, doActions )
+                return ret
+            except ParseException as err:
+                if err.loc > maxExcLoc:
+                    maxException = err
+                    maxExcLoc = err.loc
+            except IndexError:
+                if len(instring) > maxExcLoc:
+                    maxException = ParseException(instring,len(instring),e.errmsg,self)
+                    maxExcLoc = len(instring)
+
+        # only got here if no expression matched, raise exception for match that made it the furthest
+        else:
+            if maxException is not None:
+                maxException.msg = self.errmsg
+                raise maxException
+            else:
+                raise ParseException(instring, loc, "no defined alternatives to match", self)
+
+    def __ior__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        return self.append( other ) #MatchFirst( [ self, other ] )
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class Each(ParseExpression):
+    """
+    Requires all given C{ParseExpression}s to be found, but in any order.
+    Expressions may be separated by whitespace.
+    May be constructed using the C{'&'} operator.
+
+    Example::
+        color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN")
+        shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON")
+        integer = Word(nums)
+        shape_attr = "shape:" + shape_type("shape")
+        posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn")
+        color_attr = "color:" + color("color")
+        size_attr = "size:" + integer("size")
+
+        # use Each (using operator '&') to accept attributes in any order
+        # (shape and posn are required, color and size are optional)
+        shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr)
+
+        shape_spec.runTests('''
+            shape: SQUARE color: BLACK posn: 100, 120
+            shape: CIRCLE size: 50 color: BLUE posn: 50,80
+            color:GREEN size:20 shape:TRIANGLE posn:20,40
+            '''
+            )
+    prints::
+        shape: SQUARE color: BLACK posn: 100, 120
+        ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']]
+        - color: BLACK
+        - posn: ['100', ',', '120']
+          - x: 100
+          - y: 120
+        - shape: SQUARE
+
+
+        shape: CIRCLE size: 50 color: BLUE posn: 50,80
+        ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']]
+        - color: BLUE
+        - posn: ['50', ',', '80']
+          - x: 50
+          - y: 80
+        - shape: CIRCLE
+        - size: 50
+
+
+        color: GREEN size: 20 shape: TRIANGLE posn: 20,40
+        ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']]
+        - color: GREEN
+        - posn: ['20', ',', '40']
+          - x: 20
+          - y: 40
+        - shape: TRIANGLE
+        - size: 20
+    """
+    def __init__( self, exprs, savelist = True ):
+        super(Each,self).__init__(exprs, savelist)
+        self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
+        self.skipWhitespace = True
+        self.initExprGroups = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.initExprGroups:
+            self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional))
+            opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ]
+            opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)]
+            self.optionals = opt1 + opt2
+            self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ]
+            self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ]
+            self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ]
+            self.required += self.multirequired
+            self.initExprGroups = False
+        tmpLoc = loc
+        tmpReqd = self.required[:]
+        tmpOpt  = self.optionals[:]
+        matchOrder = []
+
+        keepMatching = True
+        while keepMatching:
+            tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
+            failed = []
+            for e in tmpExprs:
+                try:
+                    tmpLoc = e.tryParse( instring, tmpLoc )
+                except ParseException:
+                    failed.append(e)
+                else:
+                    matchOrder.append(self.opt1map.get(id(e),e))
+                    if e in tmpReqd:
+                        tmpReqd.remove(e)
+                    elif e in tmpOpt:
+                        tmpOpt.remove(e)
+            if len(failed) == len(tmpExprs):
+                keepMatching = False
+
+        if tmpReqd:
+            missing = ", ".join(_ustr(e) for e in tmpReqd)
+            raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing )
+
+        # add any unmatched Optionals, in case they have default values defined
+        matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt]
+
+        resultlist = []
+        for e in matchOrder:
+            loc,results = e._parse(instring,loc,doActions)
+            resultlist.append(results)
+
+        finalResults = sum(resultlist, ParseResults([]))
+        return loc, finalResults
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class ParseElementEnhance(ParserElement):
+    """
+    Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.
+    """
+    def __init__( self, expr, savelist=False ):
+        super(ParseElementEnhance,self).__init__(savelist)
+        if isinstance( expr, basestring ):
+            if issubclass(ParserElement._literalStringClass, Token):
+                expr = ParserElement._literalStringClass(expr)
+            else:
+                expr = ParserElement._literalStringClass(Literal(expr))
+        self.expr = expr
+        self.strRepr = None
+        if expr is not None:
+            self.mayIndexError = expr.mayIndexError
+            self.mayReturnEmpty = expr.mayReturnEmpty
+            self.setWhitespaceChars( expr.whiteChars )
+            self.skipWhitespace = expr.skipWhitespace
+            self.saveAsList = expr.saveAsList
+            self.callPreparse = expr.callPreparse
+            self.ignoreExprs.extend(expr.ignoreExprs)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.expr is not None:
+            return self.expr._parse( instring, loc, doActions, callPreParse=False )
+        else:
+            raise ParseException("",loc,self.errmsg,self)
+
+    def leaveWhitespace( self ):
+        self.skipWhitespace = False
+        self.expr = self.expr.copy()
+        if self.expr is not None:
+            self.expr.leaveWhitespace()
+        return self
+
+    def ignore( self, other ):
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                super( ParseElementEnhance, self).ignore( other )
+                if self.expr is not None:
+                    self.expr.ignore( self.ignoreExprs[-1] )
+        else:
+            super( ParseElementEnhance, self).ignore( other )
+            if self.expr is not None:
+                self.expr.ignore( self.ignoreExprs[-1] )
+        return self
+
+    def streamline( self ):
+        super(ParseElementEnhance,self).streamline()
+        if self.expr is not None:
+            self.expr.streamline()
+        return self
+
+    def checkRecursion( self, parseElementList ):
+        if self in parseElementList:
+            raise RecursiveGrammarException( parseElementList+[self] )
+        subRecCheckList = parseElementList[:] + [ self ]
+        if self.expr is not None:
+            self.expr.checkRecursion( subRecCheckList )
+
+    def validate( self, validateTrace=[] ):
+        tmp = validateTrace[:]+[self]
+        if self.expr is not None:
+            self.expr.validate(tmp)
+        self.checkRecursion( [] )
+
+    def __str__( self ):
+        try:
+            return super(ParseElementEnhance,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None and self.expr is not None:
+            self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) )
+        return self.strRepr
+
+
+class FollowedBy(ParseElementEnhance):
+    """
+    Lookahead matching of the given parse expression.  C{FollowedBy}
+    does I{not} advance the parsing position within the input string, it only
+    verifies that the specified parse expression matches at the current
+    position.  C{FollowedBy} always returns a null token list.
+
+    Example::
+        # use FollowedBy to match a label only if it is followed by a ':'
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+
+        OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint()
+    prints::
+        [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']]
+    """
+    def __init__( self, expr ):
+        super(FollowedBy,self).__init__(expr)
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        self.expr.tryParse( instring, loc )
+        return loc, []
+
+
+class NotAny(ParseElementEnhance):
+    """
+    Lookahead to disallow matching with the given parse expression.  C{NotAny}
+    does I{not} advance the parsing position within the input string, it only
+    verifies that the specified parse expression does I{not} match at the current
+    position.  Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny}
+    always returns a null token list.  May be constructed using the '~' operator.
+
+    Example::
+
+    """
+    def __init__( self, expr ):
+        super(NotAny,self).__init__(expr)
+        #~ self.leaveWhitespace()
+        self.skipWhitespace = False  # do NOT use self.leaveWhitespace(), don't want to propagate to exprs
+        self.mayReturnEmpty = True
+        self.errmsg = "Found unwanted token, "+_ustr(self.expr)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.expr.canParseNext(instring, loc):
+            raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "~{" + _ustr(self.expr) + "}"
+
+        return self.strRepr
+
+class _MultipleMatch(ParseElementEnhance):
+    def __init__( self, expr, stopOn=None):
+        super(_MultipleMatch, self).__init__(expr)
+        self.saveAsList = True
+        ender = stopOn
+        if isinstance(ender, basestring):
+            ender = ParserElement._literalStringClass(ender)
+        self.not_ender = ~ender if ender is not None else None
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        self_expr_parse = self.expr._parse
+        self_skip_ignorables = self._skipIgnorables
+        check_ender = self.not_ender is not None
+        if check_ender:
+            try_not_ender = self.not_ender.tryParse
+
+        # must be at least one (but first see if we are the stopOn sentinel;
+        # if so, fail)
+        if check_ender:
+            try_not_ender(instring, loc)
+        loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False )
+        try:
+            hasIgnoreExprs = (not not self.ignoreExprs)
+            while 1:
+                if check_ender:
+                    try_not_ender(instring, loc)
+                if hasIgnoreExprs:
+                    preloc = self_skip_ignorables( instring, loc )
+                else:
+                    preloc = loc
+                loc, tmptokens = self_expr_parse( instring, preloc, doActions )
+                if tmptokens or tmptokens.haskeys():
+                    tokens += tmptokens
+        except (ParseException,IndexError):
+            pass
+
+        return loc, tokens
+
+class OneOrMore(_MultipleMatch):
+    """
+    Repetition of one or more of the given expression.
+
+    Parameters:
+     - expr - expression that must match one or more times
+     - stopOn - (default=C{None}) - expression for a terminating sentinel
+          (only required if the sentinel would ordinarily match the repetition
+          expression)
+
+    Example::
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
+
+        text = "shape: SQUARE posn: upper left color: BLACK"
+        OneOrMore(attr_expr).parseString(text).pprint()  # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']]
+
+        # use stopOn attribute for OneOrMore to avoid reading label string as part of the data
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
+
+        # could also be written as
+        (attr_expr * (1,)).parseString(text).pprint()
+    """
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + _ustr(self.expr) + "}..."
+
+        return self.strRepr
+
+class ZeroOrMore(_MultipleMatch):
+    """
+    Optional repetition of zero or more of the given expression.
+
+    Parameters:
+     - expr - expression that must match zero or more times
+     - stopOn - (default=C{None}) - expression for a terminating sentinel
+          (only required if the sentinel would ordinarily match the repetition
+          expression)
+
+    Example: similar to L{OneOrMore}
+    """
+    def __init__( self, expr, stopOn=None):
+        super(ZeroOrMore,self).__init__(expr, stopOn=stopOn)
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        try:
+            return super(ZeroOrMore, self).parseImpl(instring, loc, doActions)
+        except (ParseException,IndexError):
+            return loc, []
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "[" + _ustr(self.expr) + "]..."
+
+        return self.strRepr
+
+class _NullToken(object):
+    def __bool__(self):
+        return False
+    __nonzero__ = __bool__
+    def __str__(self):
+        return ""
+
+_optionalNotMatched = _NullToken()
+class Optional(ParseElementEnhance):
+    """
+    Optional matching of the given expression.
+
+    Parameters:
+     - expr - expression that must match zero or more times
+     - default (optional) - value to be returned if the optional expression is not found.
+
+    Example::
+        # US postal code can be a 5-digit zip, plus optional 4-digit qualifier
+        zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4)))
+        zip.runTests('''
+            # traditional ZIP code
+            12345
+
+            # ZIP+4 form
+            12101-0001
+
+            # invalid ZIP
+            98765-
+            ''')
+    prints::
+        # traditional ZIP code
+        12345
+        ['12345']
+
+        # ZIP+4 form
+        12101-0001
+        ['12101-0001']
+
+        # invalid ZIP
+        98765-
+             ^
+        FAIL: Expected end of text (at char 5), (line:1, col:6)
+    """
+    def __init__( self, expr, default=_optionalNotMatched ):
+        super(Optional,self).__init__( expr, savelist=False )
+        self.saveAsList = self.expr.saveAsList
+        self.defaultValue = default
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        try:
+            loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
+        except (ParseException,IndexError):
+            if self.defaultValue is not _optionalNotMatched:
+                if self.expr.resultsName:
+                    tokens = ParseResults([ self.defaultValue ])
+                    tokens[self.expr.resultsName] = self.defaultValue
+                else:
+                    tokens = [ self.defaultValue ]
+            else:
+                tokens = []
+        return loc, tokens
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "[" + _ustr(self.expr) + "]"
+
+        return self.strRepr
+
+class SkipTo(ParseElementEnhance):
+    """
+    Token for skipping over all undefined text until the matched expression is found.
+
+    Parameters:
+     - expr - target expression marking the end of the data to be skipped
+     - include - (default=C{False}) if True, the target expression is also parsed
+          (the skipped text and target expression are returned as a 2-element list).
+     - ignore - (default=C{None}) used to define grammars (typically quoted strings and
+          comments) that might contain false matches to the target expression
+     - failOn - (default=C{None}) define expressions that are not allowed to be
+          included in the skipped test; if found before the target expression is found,
+          the SkipTo is not a match
+
+    Example::
+        report = '''
+            Outstanding Issues Report - 1 Jan 2000
+
+               # | Severity | Description                               |  Days Open
+            -----+----------+-------------------------------------------+-----------
+             101 | Critical | Intermittent system crash                 |          6
+              94 | Cosmetic | Spelling error on Login ('log|n')         |         14
+              79 | Minor    | System slow when running too many reports |         47
+            '''
+        integer = Word(nums)
+        SEP = Suppress('|')
+        # use SkipTo to simply match everything up until the next SEP
+        # - ignore quoted strings, so that a '|' character inside a quoted string does not match
+        # - parse action will call token.strip() for each matched token, i.e., the description body
+        string_data = SkipTo(SEP, ignore=quotedString)
+        string_data.setParseAction(tokenMap(str.strip))
+        ticket_expr = (integer("issue_num") + SEP
+                      + string_data("sev") + SEP
+                      + string_data("desc") + SEP
+                      + integer("days_open"))
+
+        for tkt in ticket_expr.searchString(report):
+            print tkt.dump()
+    prints::
+        ['101', 'Critical', 'Intermittent system crash', '6']
+        - days_open: 6
+        - desc: Intermittent system crash
+        - issue_num: 101
+        - sev: Critical
+        ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14']
+        - days_open: 14
+        - desc: Spelling error on Login ('log|n')
+        - issue_num: 94
+        - sev: Cosmetic
+        ['79', 'Minor', 'System slow when running too many reports', '47']
+        - days_open: 47
+        - desc: System slow when running too many reports
+        - issue_num: 79
+        - sev: Minor
+    """
+    def __init__( self, other, include=False, ignore=None, failOn=None ):
+        super( SkipTo, self ).__init__( other )
+        self.ignoreExpr = ignore
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+        self.includeMatch = include
+        self.asList = False
+        if isinstance(failOn, basestring):
+            self.failOn = ParserElement._literalStringClass(failOn)
+        else:
+            self.failOn = failOn
+        self.errmsg = "No match found for "+_ustr(self.expr)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        startloc = loc
+        instrlen = len(instring)
+        expr = self.expr
+        expr_parse = self.expr._parse
+        self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None
+        self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None
+
+        tmploc = loc
+        while tmploc <= instrlen:
+            if self_failOn_canParseNext is not None:
+                # break if failOn expression matches
+                if self_failOn_canParseNext(instring, tmploc):
+                    break
+
+            if self_ignoreExpr_tryParse is not None:
+                # advance past ignore expressions
+                while 1:
+                    try:
+                        tmploc = self_ignoreExpr_tryParse(instring, tmploc)
+                    except ParseBaseException:
+                        break
+
+            try:
+                expr_parse(instring, tmploc, doActions=False, callPreParse=False)
+            except (ParseException, IndexError):
+                # no match, advance loc in string
+                tmploc += 1
+            else:
+                # matched skipto expr, done
+                break
+
+        else:
+            # ran off the end of the input string without matching skipto expr, fail
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        # build up return values
+        loc = tmploc
+        skiptext = instring[startloc:loc]
+        skipresult = ParseResults(skiptext)
+
+        if self.includeMatch:
+            loc, mat = expr_parse(instring,loc,doActions,callPreParse=False)
+            skipresult += mat
+
+        return loc, skipresult
+
+class Forward(ParseElementEnhance):
+    """
+    Forward declaration of an expression to be defined later -
+    used for recursive grammars, such as algebraic infix notation.
+    When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator.
+
+    Note: take care when assigning to C{Forward} not to overlook precedence of operators.
+    Specifically, '|' has a lower precedence than '<<', so that::
+        fwdExpr << a | b | c
+    will actually be evaluated as::
+        (fwdExpr << a) | b | c
+    thereby leaving b and c out as parseable alternatives.  It is recommended that you
+    explicitly group the values inserted into the C{Forward}::
+        fwdExpr << (a | b | c)
+    Converting to use the '<<=' operator instead will avoid this problem.
+
+    See L{ParseResults.pprint} for an example of a recursive parser created using
+    C{Forward}.
+    """
+    def __init__( self, other=None ):
+        super(Forward,self).__init__( other, savelist=False )
+
+    def __lshift__( self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass(other)
+        self.expr = other
+        self.strRepr = None
+        self.mayIndexError = self.expr.mayIndexError
+        self.mayReturnEmpty = self.expr.mayReturnEmpty
+        self.setWhitespaceChars( self.expr.whiteChars )
+        self.skipWhitespace = self.expr.skipWhitespace
+        self.saveAsList = self.expr.saveAsList
+        self.ignoreExprs.extend(self.expr.ignoreExprs)
+        return self
+
+    def __ilshift__(self, other):
+        return self << other
+
+    def leaveWhitespace( self ):
+        self.skipWhitespace = False
+        return self
+
+    def streamline( self ):
+        if not self.streamlined:
+            self.streamlined = True
+            if self.expr is not None:
+                self.expr.streamline()
+        return self
+
+    def validate( self, validateTrace=[] ):
+        if self not in validateTrace:
+            tmp = validateTrace[:]+[self]
+            if self.expr is not None:
+                self.expr.validate(tmp)
+        self.checkRecursion([])
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+        return self.__class__.__name__ + ": ..."
+
+        # stubbed out for now - creates awful memory and perf issues
+        self._revertClass = self.__class__
+        self.__class__ = _ForwardNoRecurse
+        try:
+            if self.expr is not None:
+                retString = _ustr(self.expr)
+            else:
+                retString = "None"
+        finally:
+            self.__class__ = self._revertClass
+        return self.__class__.__name__ + ": " + retString
+
+    def copy(self):
+        if self.expr is not None:
+            return super(Forward,self).copy()
+        else:
+            ret = Forward()
+            ret <<= self
+            return ret
+
+class _ForwardNoRecurse(Forward):
+    def __str__( self ):
+        return "..."
+
+class TokenConverter(ParseElementEnhance):
+    """
+    Abstract subclass of C{ParseExpression}, for converting parsed results.
+    """
+    def __init__( self, expr, savelist=False ):
+        super(TokenConverter,self).__init__( expr )#, savelist )
+        self.saveAsList = False
+
+class Combine(TokenConverter):
+    """
+    Converter to concatenate all matching tokens to a single string.
+    By default, the matching patterns must also be contiguous in the input string;
+    this can be disabled by specifying C{'adjacent=False'} in the constructor.
+
+    Example::
+        real = Word(nums) + '.' + Word(nums)
+        print(real.parseString('3.1416')) # -> ['3', '.', '1416']
+        # will also erroneously match the following
+        print(real.parseString('3. 1416')) # -> ['3', '.', '1416']
+
+        real = Combine(Word(nums) + '.' + Word(nums))
+        print(real.parseString('3.1416')) # -> ['3.1416']
+        # no match when there are internal spaces
+        print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...)
+    """
+    def __init__( self, expr, joinString="", adjacent=True ):
+        super(Combine,self).__init__( expr )
+        # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself
+        if adjacent:
+            self.leaveWhitespace()
+        self.adjacent = adjacent
+        self.skipWhitespace = True
+        self.joinString = joinString
+        self.callPreparse = True
+
+    def ignore( self, other ):
+        if self.adjacent:
+            ParserElement.ignore(self, other)
+        else:
+            super( Combine, self).ignore( other )
+        return self
+
+    def postParse( self, instring, loc, tokenlist ):
+        retToks = tokenlist.copy()
+        del retToks[:]
+        retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults)
+
+        if self.resultsName and retToks.haskeys():
+            return [ retToks ]
+        else:
+            return retToks
+
+class Group(TokenConverter):
+    """
+    Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.
+
+    Example::
+        ident = Word(alphas)
+        num = Word(nums)
+        term = ident | num
+        func = ident + Optional(delimitedList(term))
+        print(func.parseString("fn a,b,100"))  # -> ['fn', 'a', 'b', '100']
+
+        func = ident + Group(Optional(delimitedList(term)))
+        print(func.parseString("fn a,b,100"))  # -> ['fn', ['a', 'b', '100']]
+    """
+    def __init__( self, expr ):
+        super(Group,self).__init__( expr )
+        self.saveAsList = True
+
+    def postParse( self, instring, loc, tokenlist ):
+        return [ tokenlist ]
+
+class Dict(TokenConverter):
+    """
+    Converter to return a repetitive expression as a list, but also as a dictionary.
+    Each element can also be referenced using the first token in the expression as its key.
+    Useful for tabular report scraping when the first column can be used as a item key.
+
+    Example::
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
+
+        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
+        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+
+        # print attributes as plain groups
+        print(OneOrMore(attr_expr).parseString(text).dump())
+
+        # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names
+        result = Dict(OneOrMore(Group(attr_expr))).parseString(text)
+        print(result.dump())
+
+        # access named fields as dict entries, or output as dict
+        print(result['shape'])
+        print(result.asDict())
+    prints::
+        ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
+
+        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
+        - color: light blue
+        - posn: upper left
+        - shape: SQUARE
+        - texture: burlap
+        SQUARE
+        {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'}
+    See more examples at L{ParseResults} of accessing fields by results name.
+    """
+    def __init__( self, expr ):
+        super(Dict,self).__init__( expr )
+        self.saveAsList = True
+
+    def postParse( self, instring, loc, tokenlist ):
+        for i,tok in enumerate(tokenlist):
+            if len(tok) == 0:
+                continue
+            ikey = tok[0]
+            if isinstance(ikey,int):
+                ikey = _ustr(tok[0]).strip()
+            if len(tok)==1:
+                tokenlist[ikey] = _ParseResultsWithOffset("",i)
+            elif len(tok)==2 and not isinstance(tok[1],ParseResults):
+                tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i)
+            else:
+                dictvalue = tok.copy() #ParseResults(i)
+                del dictvalue[0]
+                if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()):
+                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i)
+                else:
+                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i)
+
+        if self.resultsName:
+            return [ tokenlist ]
+        else:
+            return tokenlist
+
+
+class Suppress(TokenConverter):
+    """
+    Converter for ignoring the results of a parsed expression.
+
+    Example::
+        source = "a, b, c,d"
+        wd = Word(alphas)
+        wd_list1 = wd + ZeroOrMore(',' + wd)
+        print(wd_list1.parseString(source))
+
+        # often, delimiters that are useful during parsing are just in the
+        # way afterward - use Suppress to keep them out of the parsed output
+        wd_list2 = wd + ZeroOrMore(Suppress(',') + wd)
+        print(wd_list2.parseString(source))
+    prints::
+        ['a', ',', 'b', ',', 'c', ',', 'd']
+        ['a', 'b', 'c', 'd']
+    (See also L{delimitedList}.)
+    """
+    def postParse( self, instring, loc, tokenlist ):
+        return []
+
+    def suppress( self ):
+        return self
+
+
+class OnlyOnce(object):
+    """
+    Wrapper for parse actions, to ensure they are only called once.
+    """
+    def __init__(self, methodCall):
+        self.callable = _trim_arity(methodCall)
+        self.called = False
+    def __call__(self,s,l,t):
+        if not self.called:
+            results = self.callable(s,l,t)
+            self.called = True
+            return results
+        raise ParseException(s,l,"")
+    def reset(self):
+        self.called = False
+
+def traceParseAction(f):
+    """
+    Decorator for debugging parse actions.
+
+    When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".}
+    When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised.
+
+    Example::
+        wd = Word(alphas)
+
+        @traceParseAction
+        def remove_duplicate_chars(tokens):
+            return ''.join(sorted(set(''.join(tokens)))
+
+        wds = OneOrMore(wd).setParseAction(remove_duplicate_chars)
+        print(wds.parseString("slkdjs sld sldd sdlf sdljf"))
+    prints::
+        >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {}))
+        <<leaving remove_duplicate_chars (ret: 'dfjkls')
+        ['dfjkls']
+    """
+    f = _trim_arity(f)
+    def z(*paArgs):
+        thisFunc = f.__name__
+        s,l,t = paArgs[-3:]
+        if len(paArgs)>3:
+            thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc
+        sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) )
+        try:
+            ret = f(*paArgs)
+        except Exception as exc:
+            sys.stderr.write( "<<leaving %s (exception: %s)\n" % (thisFunc,exc) )
+            raise
+        sys.stderr.write( "<<leaving %s (ret: %r)\n" % (thisFunc,ret) )
+        return ret
+    try:
+        z.__name__ = f.__name__
+    except AttributeError:
+        pass
+    return z
+
+#
+# global helpers
+#
+def delimitedList( expr, delim=",", combine=False ):
+    """
+    Helper to define a delimited list of expressions - the delimiter defaults to ','.
+    By default, the list elements and delimiters can have intervening whitespace, and
+    comments, but this can be overridden by passing C{combine=True} in the constructor.
+    If C{combine} is set to C{True}, the matching tokens are returned as a single token
+    string, with the delimiters included; otherwise, the matching tokens are returned
+    as a list of tokens, with the delimiters suppressed.
+
+    Example::
+        delimitedList(Word(alphas)).parseString("aa,bb,cc") # -> ['aa', 'bb', 'cc']
+        delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
+    """
+    dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..."
+    if combine:
+        return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName)
+    else:
+        return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName)
+
+def countedArray( expr, intExpr=None ):
+    """
+    Helper to define a counted list of expressions.
+    This helper defines a pattern of the form::
+        integer expr expr expr...
+    where the leading integer tells how many expr expressions follow.
+    The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed.
+
+    If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value.
+
+    Example::
+        countedArray(Word(alphas)).parseString('2 ab cd ef')  # -> ['ab', 'cd']
+
+        # in this parser, the leading integer value is given in binary,
+        # '10' indicating that 2 values are in the array
+        binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2))
+        countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef')  # -> ['ab', 'cd']
+    """
+    arrayExpr = Forward()
+    def countFieldParseAction(s,l,t):
+        n = t[0]
+        arrayExpr << (n and Group(And([expr]*n)) or Group(empty))
+        return []
+    if intExpr is None:
+        intExpr = Word(nums).setParseAction(lambda t:int(t[0]))
+    else:
+        intExpr = intExpr.copy()
+    intExpr.setName("arrayLen")
+    intExpr.addParseAction(countFieldParseAction, callDuringTry=True)
+    return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...')
+
+def _flatten(L):
+    ret = []
+    for i in L:
+        if isinstance(i,list):
+            ret.extend(_flatten(i))
+        else:
+            ret.append(i)
+    return ret
+
+def matchPreviousLiteral(expr):
+    """
+    Helper to define an expression that is indirectly defined from
+    the tokens matched in a previous expression, that is, it looks
+    for a 'repeat' of a previous expression.  For example::
+        first = Word(nums)
+        second = matchPreviousLiteral(first)
+        matchExpr = first + ":" + second
+    will match C{"1:1"}, but not C{"1:2"}.  Because this matches a
+    previous literal, will also match the leading C{"1:1"} in C{"1:10"}.
+    If this is not desired, use C{matchPreviousExpr}.
+    Do I{not} use with packrat parsing enabled.
+    """
+    rep = Forward()
+    def copyTokenToRepeater(s,l,t):
+        if t:
+            if len(t) == 1:
+                rep << t[0]
+            else:
+                # flatten t tokens
+                tflat = _flatten(t.asList())
+                rep << And(Literal(tt) for tt in tflat)
+        else:
+            rep << Empty()
+    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
+    rep.setName('(prev) ' + _ustr(expr))
+    return rep
+
+def matchPreviousExpr(expr):
+    """
+    Helper to define an expression that is indirectly defined from
+    the tokens matched in a previous expression, that is, it looks
+    for a 'repeat' of a previous expression.  For example::
+        first = Word(nums)
+        second = matchPreviousExpr(first)
+        matchExpr = first + ":" + second
+    will match C{"1:1"}, but not C{"1:2"}.  Because this matches by
+    expressions, will I{not} match the leading C{"1:1"} in C{"1:10"};
+    the expressions are evaluated first, and then compared, so
+    C{"1"} is compared with C{"10"}.
+    Do I{not} use with packrat parsing enabled.
+    """
+    rep = Forward()
+    e2 = expr.copy()
+    rep <<= e2
+    def copyTokenToRepeater(s,l,t):
+        matchTokens = _flatten(t.asList())
+        def mustMatchTheseTokens(s,l,t):
+            theseTokens = _flatten(t.asList())
+            if  theseTokens != matchTokens:
+                raise ParseException("",0,"")
+        rep.setParseAction( mustMatchTheseTokens, callDuringTry=True )
+    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
+    rep.setName('(prev) ' + _ustr(expr))
+    return rep
+
+def _escapeRegexRangeChars(s):
+    #~  escape these chars: ^-]
+    for c in r"\^-]":
+        s = s.replace(c,_bslash+c)
+    s = s.replace("\n",r"\n")
+    s = s.replace("\t",r"\t")
+    return _ustr(s)
+
+def oneOf( strs, caseless=False, useRegex=True ):
+    """
+    Helper to quickly define a set of alternative Literals, and makes sure to do
+    longest-first testing when there is a conflict, regardless of the input order,
+    but returns a C{L{MatchFirst}} for best performance.
+
+    Parameters:
+     - strs - a string of space-delimited literals, or a collection of string literals
+     - caseless - (default=C{False}) - treat all literals as caseless
+     - useRegex - (default=C{True}) - as an optimization, will generate a Regex
+          object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or
+          if creating a C{Regex} raises an exception)
+
+    Example::
+        comp_oper = oneOf("< = > <= >= !=")
+        var = Word(alphas)
+        number = Word(nums)
+        term = var | number
+        comparison_expr = term + comp_oper + term
+        print(comparison_expr.searchString("B = 12  AA=23 B<=AA AA>12"))
+    prints::
+        [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
+    """
+    if caseless:
+        isequal = ( lambda a,b: a.upper() == b.upper() )
+        masks = ( lambda a,b: b.upper().startswith(a.upper()) )
+        parseElementClass = CaselessLiteral
+    else:
+        isequal = ( lambda a,b: a == b )
+        masks = ( lambda a,b: b.startswith(a) )
+        parseElementClass = Literal
+
+    symbols = []
+    if isinstance(strs,basestring):
+        symbols = strs.split()
+    elif isinstance(strs, collections.Iterable):
+        symbols = list(strs)
+    else:
+        warnings.warn("Invalid argument to oneOf, expected string or iterable",
+                SyntaxWarning, stacklevel=2)
+    if not symbols:
+        return NoMatch()
+
+    i = 0
+    while i < len(symbols)-1:
+        cur = symbols[i]
+        for j,other in enumerate(symbols[i+1:]):
+            if ( isequal(other, cur) ):
+                del symbols[i+j+1]
+                break
+            elif ( masks(cur, other) ):
+                del symbols[i+j+1]
+                symbols.insert(i,other)
+                cur = other
+                break
+        else:
+            i += 1
+
+    if not caseless and useRegex:
+        #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] ))
+        try:
+            if len(symbols)==len("".join(symbols)):
+                return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols))
+            else:
+                return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols))
+        except Exception:
+            warnings.warn("Exception creating Regex for oneOf, building MatchFirst",
+                    SyntaxWarning, stacklevel=2)
+
+
+    # last resort, just use MatchFirst
+    return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols))
+
+def dictOf( key, value ):
+    """
+    Helper to easily and clearly define a dictionary by specifying the respective patterns
+    for the key and value.  Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens
+    in the proper order.  The key pattern can include delimiting markers or punctuation,
+    as long as they are suppressed, thereby leaving the significant key text.  The value
+    pattern can include named results, so that the C{Dict} results can include named token
+    fields.
+
+    Example::
+        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
+        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        print(OneOrMore(attr_expr).parseString(text).dump())
+
+        attr_label = label
+        attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)
+
+        # similar to Dict, but simpler call format
+        result = dictOf(attr_label, attr_value).parseString(text)
+        print(result.dump())
+        print(result['shape'])
+        print(result.shape)  # object attribute access works too
+        print(result.asDict())
+    prints::
+        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
+        - color: light blue
+        - posn: upper left
+        - shape: SQUARE
+        - texture: burlap
+        SQUARE
+        SQUARE
+        {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'}
+    """
+    return Dict( ZeroOrMore( Group ( key + value ) ) )
+
+def originalTextFor(expr, asString=True):
+    """
+    Helper to return the original, untokenized text for a given expression.  Useful to
+    restore the parsed fields of an HTML start tag into the raw tag text itself, or to
+    revert separate tokens with intervening whitespace back to the original matching
+    input text. By default, returns astring containing the original parsed text.
+
+    If the optional C{asString} argument is passed as C{False}, then the return value is a
+    C{L{ParseResults}} containing any results names that were originally matched, and a
+    single token containing the original matched text from the input string.  So if
+    the expression passed to C{L{originalTextFor}} contains expressions with defined
+    results names, you must set C{asString} to C{False} if you want to preserve those
+    results name values.
+
+    Example::
+        src = "this is test <b> bold <i>text</i> </b> normal text "
+        for tag in ("b","i"):
+            opener,closer = makeHTMLTags(tag)
+            patt = originalTextFor(opener + SkipTo(closer) + closer)
+            print(patt.searchString(src)[0])
+    prints::
+        ['<b> bold <i>text</i> </b>']
+        ['<i>text</i>']
+    """
+    locMarker = Empty().setParseAction(lambda s,loc,t: loc)
+    endlocMarker = locMarker.copy()
+    endlocMarker.callPreparse = False
+    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
+    if asString:
+        extractText = lambda s,l,t: s[t._original_start:t._original_end]
+    else:
+        def extractText(s,l,t):
+            t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]]
+    matchExpr.setParseAction(extractText)
+    matchExpr.ignoreExprs = expr.ignoreExprs
+    return matchExpr
+
+def ungroup(expr):
+    """
+    Helper to undo pyparsing's default grouping of And expressions, even
+    if all but one are non-empty.
+    """
+    return TokenConverter(expr).setParseAction(lambda t:t[0])
+
+def locatedExpr(expr):
+    """
+    Helper to decorate a returned token with its starting and ending locations in the input string.
+    This helper adds the following results names:
+     - locn_start = location where matched expression begins
+     - locn_end = location where matched expression ends
+     - value = the actual parsed results
+
+    Be careful if the input text contains C{<TAB>} characters, you may want to call
+    C{L{ParserElement.parseWithTabs}}
+
+    Example::
+        wd = Word(alphas)
+        for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"):
+            print(match)
+    prints::
+        [[0, 'ljsdf', 5]]
+        [[8, 'lksdjjf', 15]]
+        [[18, 'lkkjj', 23]]
+    """
+    locator = Empty().setParseAction(lambda s,l,t: l)
+    return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end"))
+
+
+# convenience constants for positional expressions
+empty       = Empty().setName("empty")
+lineStart   = LineStart().setName("lineStart")
+lineEnd     = LineEnd().setName("lineEnd")
+stringStart = StringStart().setName("stringStart")
+stringEnd   = StringEnd().setName("stringEnd")
+
+_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
+_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16)))
+_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8)))
+_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(printables, excludeChars=r'\]', exact=1) | Regex(r"\w", re.UNICODE)
+_charRange = Group(_singleChar + Suppress("-") + _singleChar)
+_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
+
+def srange(s):
+    r"""
+    Helper to easily define string ranges for use in Word construction.  Borrows
+    syntax from regexp '[]' string range definitions::
+        srange("[0-9]")   -> "0123456789"
+        srange("[a-z]")   -> "abcdefghijklmnopqrstuvwxyz"
+        srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_"
+    The input string must be enclosed in []'s, and the returned string is the expanded
+    character set joined into a single string.
+    The values enclosed in the []'s may be:
+     - a single character
+     - an escaped character with a leading backslash (such as C{\-} or C{\]})
+     - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character)
+         (C{\0x##} is also supported for backwards compatibility)
+     - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character)
+     - a range of any of the above, separated by a dash (C{'a-z'}, etc.)
+     - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.)
+    """
+    _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1))
+    try:
+        return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body)
+    except Exception:
+        return ""
+
+def matchOnlyAtCol(n):
+    """
+    Helper method for defining parse actions that require matching at a specific
+    column in the input text.
+    """
+    def verifyCol(strg,locn,toks):
+        if col(locn,strg) != n:
+            raise ParseException(strg,locn,"matched token not at column %d" % n)
+    return verifyCol
+
+def replaceWith(replStr):
+    """
+    Helper method for common parse actions that simply return a literal value.  Especially
+    useful when used with C{L{transformString<ParserElement.transformString>}()}.
+
+    Example::
+        num = Word(nums).setParseAction(lambda toks: int(toks[0]))
+        na = oneOf("N/A NA").setParseAction(replaceWith(math.nan))
+        term = na | num
+
+        OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234]
+    """
+    return lambda s,l,t: [replStr]
+
+def removeQuotes(s,l,t):
+    """
+    Helper parse action for removing quotation marks from parsed quoted strings.
+
+    Example::
+        # by default, quotation marks are included in parsed results
+        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"]
+
+        # use removeQuotes to strip quotation marks from parsed results
+        quotedString.setParseAction(removeQuotes)
+        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"]
+    """
+    return t[0][1:-1]
+
+def tokenMap(func, *args):
+    """
+    Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional
+    args are passed, they are forwarded to the given function as additional arguments after
+    the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the
+    parsed data to an integer using base 16.
+
+    Example (compare the last to example in L{ParserElement.transformString}::
+        hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16))
+        hex_ints.runTests('''
+            00 11 22 aa FF 0a 0d 1a
+            ''')
+
+        upperword = Word(alphas).setParseAction(tokenMap(str.upper))
+        OneOrMore(upperword).runTests('''
+            my kingdom for a horse
+            ''')
+
+        wd = Word(alphas).setParseAction(tokenMap(str.title))
+        OneOrMore(wd).setParseAction(' '.join).runTests('''
+            now is the winter of our discontent made glorious summer by this sun of york
+            ''')
+    prints::
+        00 11 22 aa FF 0a 0d 1a
+        [0, 17, 34, 170, 255, 10, 13, 26]
+
+        my kingdom for a horse
+        ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE']
+
+        now is the winter of our discontent made glorious summer by this sun of york
+        ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York']
+    """
+    def pa(s,l,t):
+        return [func(tokn, *args) for tokn in t]
+
+    try:
+        func_name = getattr(func, '__name__',
+                            getattr(func, '__class__').__name__)
+    except Exception:
+        func_name = str(func)
+    pa.__name__ = func_name
+
+    return pa
+
+upcaseTokens = tokenMap(lambda t: _ustr(t).upper())
+"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}"""
+
+downcaseTokens = tokenMap(lambda t: _ustr(t).lower())
+"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}"""
+
+def _makeTags(tagStr, xml):
+    """Internal helper to construct opening and closing tag expressions, given a tag name"""
+    if isinstance(tagStr,basestring):
+        resname = tagStr
+        tagStr = Keyword(tagStr, caseless=not xml)
+    else:
+        resname = tagStr.name
+
+    tagAttrName = Word(alphas,alphanums+"_-:")
+    if (xml):
+        tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes )
+        openTag = Suppress("<") + tagStr("tag") + \
+                Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \
+                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
+    else:
+        printablesLessRAbrack = "".join(c for c in printables if c not in ">")
+        tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack)
+        openTag = Suppress("<") + tagStr("tag") + \
+                Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \
+                Optional( Suppress("=") + tagAttrValue ) ))) + \
+                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
+    closeTag = Combine(_L("</") + tagStr + ">")
+
+    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
+    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("</%s>" % resname)
+    openTag.tag = resname
+    closeTag.tag = resname
+    return openTag, closeTag
+
+def makeHTMLTags(tagStr):
+    """
+    Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches
+    tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values.
+
+    Example::
+        text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>'
+        # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple
+        a,a_end = makeHTMLTags("A")
+        link_expr = a + SkipTo(a_end)("link_text") + a_end
+
+        for link in link_expr.searchString(text):
+            # attributes in the <A> tag (like "href" shown here) are also accessible as named results
+            print(link.link_text, '->', link.href)
+    prints::
+        pyparsing -> http://pyparsing.wikispaces.com
+    """
+    return _makeTags( tagStr, False )
+
+def makeXMLTags(tagStr):
+    """
+    Helper to construct opening and closing tag expressions for XML, given a tag name. Matches
+    tags only in the given upper/lower case.
+
+    Example: similar to L{makeHTMLTags}
+    """
+    return _makeTags( tagStr, True )
+
+def withAttribute(*args,**attrDict):
+    """
+    Helper to create a validating parse action to be used with start tags created
+    with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag
+    with a required attribute value, to avoid false matches on common tags such as
+    C{<TD>} or C{<DIV>}.
+
+    Call C{withAttribute} with a series of attribute names and values. Specify the list
+    of filter attributes names and values as:
+     - keyword arguments, as in C{(align="right")}, or
+     - as an explicit dict with C{**} operator, when an attribute name is also a Python
+          reserved word, as in C{**{"class":"Customer", "align":"right"}}
+     - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") )
+    For attribute names with a namespace prefix, you must use the second form.  Attribute
+    names are matched insensitive to upper/lower case.
+
+    If just testing for C{class} (with or without a namespace), use C{L{withClass}}.
+
+    To verify that the attribute exists, but without specifying a value, pass
+    C{withAttribute.ANY_VALUE} as the value.
+
+    Example::
+        html = '''
+            <div>
+            Some text
+            <div type="grid">1 4 0 1 0</div>
+            <div type="graph">1,3 2,3 1,1</div>
+            <div>this has no type</div>
+            </div>
+
+        '''
+        div,div_end = makeHTMLTags("div")
+
+        # only match div tag having a type attribute with value "grid"
+        div_grid = div().setParseAction(withAttribute(type="grid"))
+        grid_expr = div_grid + SkipTo(div | div_end)("body")
+        for grid_header in grid_expr.searchString(html):
+            print(grid_header.body)
+
+        # construct a match with any div tag having a type attribute, regardless of the value
+        div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE))
+        div_expr = div_any_type + SkipTo(div | div_end)("body")
+        for div_header in div_expr.searchString(html):
+            print(div_header.body)
+    prints::
+        1 4 0 1 0
+
+        1 4 0 1 0
+        1,3 2,3 1,1
+    """
+    if args:
+        attrs = args[:]
+    else:
+        attrs = attrDict.items()
+    attrs = [(k,v) for k,v in attrs]
+    def pa(s,l,tokens):
+        for attrName,attrValue in attrs:
+            if attrName not in tokens:
+                raise ParseException(s,l,"no matching attribute " + attrName)
+            if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue:
+                raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" %
+                                            (attrName, tokens[attrName], attrValue))
+    return pa
+withAttribute.ANY_VALUE = object()
+
+def withClass(classname, namespace=''):
+    """
+    Simplified version of C{L{withAttribute}} when matching on a div class - made
+    difficult because C{class} is a reserved word in Python.
+
+    Example::
+        html = '''
+            <div>
+            Some text
+            <div class="grid">1 4 0 1 0</div>
+            <div class="graph">1,3 2,3 1,1</div>
+            <div>this &lt;div&gt; has no class</div>
+            </div>
+
+        '''
+        div,div_end = makeHTMLTags("div")
+        div_grid = div().setParseAction(withClass("grid"))
+
+        grid_expr = div_grid + SkipTo(div | div_end)("body")
+        for grid_header in grid_expr.searchString(html):
+            print(grid_header.body)
+
+        div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE))
+        div_expr = div_any_type + SkipTo(div | div_end)("body")
+        for div_header in div_expr.searchString(html):
+            print(div_header.body)
+    prints::
+        1 4 0 1 0
+
+        1 4 0 1 0
+        1,3 2,3 1,1
+    """
+    classattr = "%s:class" % namespace if namespace else "class"
+    return withAttribute(**{classattr : classname})
+
+opAssoc = _Constants()
+opAssoc.LEFT = object()
+opAssoc.RIGHT = object()
+
+def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ):
+    """
+    Helper method for constructing grammars of expressions made up of
+    operators working in a precedence hierarchy.  Operators may be unary or
+    binary, left- or right-associative.  Parse actions can also be attached
+    to operator expressions. The generated parser will also recognize the use
+    of parentheses to override operator precedences (see example below).
+
+    Note: if you define a deep operator list, you may see performance issues
+    when using infixNotation. See L{ParserElement.enablePackrat} for a
+    mechanism to potentially improve your parser performance.
+
+    Parameters:
+     - baseExpr - expression representing the most basic element for the nested
+     - opList - list of tuples, one for each operator precedence level in the
+      expression grammar; each tuple is of the form
+      (opExpr, numTerms, rightLeftAssoc, parseAction), where:
+       - opExpr is the pyparsing expression for the operator;
+          may also be a string, which will be converted to a Literal;
+          if numTerms is 3, opExpr is a tuple of two expressions, for the
+          two operators separating the 3 terms
+       - numTerms is the number of terms for this operator (must
+          be 1, 2, or 3)
+       - rightLeftAssoc is the indicator whether the operator is
+          right or left associative, using the pyparsing-defined
+          constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}.
+       - parseAction is the parse action to be associated with
+          expressions matching this operator expression (the
+          parse action tuple member may be omitted); if the parse action
+          is passed a tuple or list of functions, this is equivalent to
+          calling C{setParseAction(*fn)} (L{ParserElement.setParseAction})
+     - lpar - expression for matching left-parentheses (default=C{Suppress('(')})
+     - rpar - expression for matching right-parentheses (default=C{Suppress(')')})
+
+    Example::
+        # simple example of four-function arithmetic with ints and variable names
+        integer = pyparsing_common.signed_integer
+        varname = pyparsing_common.identifier
+
+        arith_expr = infixNotation(integer | varname,
+            [
+            ('-', 1, opAssoc.RIGHT),
+            (oneOf('* /'), 2, opAssoc.LEFT),
+            (oneOf('+ -'), 2, opAssoc.LEFT),
+            ])
+
+        arith_expr.runTests('''
+            5+3*6
+            (5+3)*6
+            -2--11
+            ''', fullDump=False)
+    prints::
+        5+3*6
+        [[5, '+', [3, '*', 6]]]
+
+        (5+3)*6
+        [[[5, '+', 3], '*', 6]]
+
+        -2--11
+        [[['-', 2], '-', ['-', 11]]]
+    """
+    ret = Forward()
+    lastExpr = baseExpr | ( lpar + ret + rpar )
+    for i,operDef in enumerate(opList):
+        opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4]
+        termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr
+        if arity == 3:
+            if opExpr is None or len(opExpr) != 2:
+                raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions")
+            opExpr1, opExpr2 = opExpr
+        thisExpr = Forward().setName(termName)
+        if rightLeftAssoc == opAssoc.LEFT:
+            if arity == 1:
+                matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) )
+            elif arity == 2:
+                if opExpr is not None:
+                    matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) )
+                else:
+                    matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) )
+            elif arity == 3:
+                matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \
+                            Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr )
+            else:
+                raise ValueError("operator must be unary (1), binary (2), or ternary (3)")
+        elif rightLeftAssoc == opAssoc.RIGHT:
+            if arity == 1:
+                # try to avoid LR with this extra test
+                if not isinstance(opExpr, Optional):
+                    opExpr = Optional(opExpr)
+                matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr )
+            elif arity == 2:
+                if opExpr is not None:
+                    matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) )
+                else:
+                    matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) )
+            elif arity == 3:
+                matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \
+                            Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr )
+            else:
+                raise ValueError("operator must be unary (1), binary (2), or ternary (3)")
+        else:
+            raise ValueError("operator must indicate right or left associativity")
+        if pa:
+            if isinstance(pa, (tuple, list)):
+                matchExpr.setParseAction(*pa)
+            else:
+                matchExpr.setParseAction(pa)
+        thisExpr <<= ( matchExpr.setName(termName) | lastExpr )
+        lastExpr = thisExpr
+    ret <<= lastExpr
+    return ret
+
+operatorPrecedence = infixNotation
+"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release."""
+
+dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes")
+sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes")
+quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'|
+                       Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes")
+unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal")
+
+def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()):
+    """
+    Helper method for defining nested lists enclosed in opening and closing
+    delimiters ("(" and ")" are the default).
+
+    Parameters:
+     - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression
+     - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression
+     - content - expression for items within the nested lists (default=C{None})
+     - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString})
+
+    If an expression is not provided for the content argument, the nested
+    expression will capture all whitespace-delimited content between delimiters
+    as a list of separate values.
+
+    Use the C{ignoreExpr} argument to define expressions that may contain
+    opening or closing characters that should not be treated as opening
+    or closing characters for nesting, such as quotedString or a comment
+    expression.  Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}.
+    The default is L{quotedString}, but if no expressions are to be ignored,
+    then pass C{None} for this argument.
+
+    Example::
+        data_type = oneOf("void int short long char float double")
+        decl_data_type = Combine(data_type + Optional(Word('*')))
+        ident = Word(alphas+'_', alphanums+'_')
+        number = pyparsing_common.number
+        arg = Group(decl_data_type + ident)
+        LPAR,RPAR = map(Suppress, "()")
+
+        code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment))
+
+        c_function = (decl_data_type("type")
+                      + ident("name")
+                      + LPAR + Optional(delimitedList(arg), [])("args") + RPAR
+                      + code_body("body"))
+        c_function.ignore(cStyleComment)
+
+        source_code = '''
+            int is_odd(int x) {
+                return (x%2);
+            }
+
+            int dec_to_hex(char hchar) {
+                if (hchar >= '0' && hchar <= '9') {
+                    return (ord(hchar)-ord('0'));
+                } else {
+                    return (10+ord(hchar)-ord('A'));
+                }
+            }
+        '''
+        for func in c_function.searchString(source_code):
+            print("%(name)s (%(type)s) args: %(args)s" % func)
+
+    prints::
+        is_odd (int) args: [['int', 'x']]
+        dec_to_hex (int) args: [['char', 'hchar']]
+    """
+    if opener == closer:
+        raise ValueError("opening and closing strings cannot be the same")
+    if content is None:
+        if isinstance(opener,basestring) and isinstance(closer,basestring):
+            if len(opener) == 1 and len(closer)==1:
+                if ignoreExpr is not None:
+                    content = (Combine(OneOrMore(~ignoreExpr +
+                                    CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1))
+                                ).setParseAction(lambda t:t[0].strip()))
+                else:
+                    content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS
+                                ).setParseAction(lambda t:t[0].strip()))
+            else:
+                if ignoreExpr is not None:
+                    content = (Combine(OneOrMore(~ignoreExpr +
+                                    ~Literal(opener) + ~Literal(closer) +
+                                    CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1))
+                                ).setParseAction(lambda t:t[0].strip()))
+                else:
+                    content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) +
+                                    CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1))
+                                ).setParseAction(lambda t:t[0].strip()))
+        else:
+            raise ValueError("opening and closing arguments must be strings if no content expression is given")
+    ret = Forward()
+    if ignoreExpr is not None:
+        ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) )
+    else:
+        ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content )  + Suppress(closer) )
+    ret.setName('nested %s%s expression' % (opener,closer))
+    return ret
+
+def indentedBlock(blockStatementExpr, indentStack, indent=True):
+    """
+    Helper method for defining space-delimited indentation blocks, such as
+    those used to define block statements in Python source code.
+
+    Parameters:
+     - blockStatementExpr - expression defining syntax of statement that
+            is repeated within the indented block
+     - indentStack - list created by caller to manage indentation stack
+            (multiple statementWithIndentedBlock expressions within a single grammar
+            should share a common indentStack)
+     - indent - boolean indicating whether block must be indented beyond the
+            the current level; set to False for block of left-most statements
+            (default=C{True})
+
+    A valid block must contain at least one C{blockStatement}.
+
+    Example::
+        data = '''
+        def A(z):
+          A1
+          B = 100
+          G = A2
+          A2
+          A3
+        B
+        def BB(a,b,c):
+          BB1
+          def BBA():
+            bba1
+            bba2
+            bba3
+        C
+        D
+        def spam(x,y):
+             def eggs(z):
+                 pass
+        '''
+
+
+        indentStack = [1]
+        stmt = Forward()
+
+        identifier = Word(alphas, alphanums)
+        funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":")
+        func_body = indentedBlock(stmt, indentStack)
+        funcDef = Group( funcDecl + func_body )
+
+        rvalue = Forward()
+        funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")")
+        rvalue << (funcCall | identifier | Word(nums))
+        assignment = Group(identifier + "=" + rvalue)
+        stmt << ( funcDef | assignment | identifier )
+
+        module_body = OneOrMore(stmt)
+
+        parseTree = module_body.parseString(data)
+        parseTree.pprint()
+    prints::
+        [['def',
+          'A',
+          ['(', 'z', ')'],
+          ':',
+          [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]],
+         'B',
+         ['def',
+          'BB',
+          ['(', 'a', 'b', 'c', ')'],
+          ':',
+          [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]],
+         'C',
+         'D',
+         ['def',
+          'spam',
+          ['(', 'x', 'y', ')'],
+          ':',
+          [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]]
+    """
+    def checkPeerIndent(s,l,t):
+        if l >= len(s): return
+        curCol = col(l,s)
+        if curCol != indentStack[-1]:
+            if curCol > indentStack[-1]:
+                raise ParseFatalException(s,l,"illegal nesting")
+            raise ParseException(s,l,"not a peer entry")
+
+    def checkSubIndent(s,l,t):
+        curCol = col(l,s)
+        if curCol > indentStack[-1]:
+            indentStack.append( curCol )
+        else:
+            raise ParseException(s,l,"not a subentry")
+
+    def checkUnindent(s,l,t):
+        if l >= len(s): return
+        curCol = col(l,s)
+        if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]):
+            raise ParseException(s,l,"not an unindent")
+        indentStack.pop()
+
+    NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress())
+    INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT')
+    PEER   = Empty().setParseAction(checkPeerIndent).setName('')
+    UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT')
+    if indent:
+        smExpr = Group( Optional(NL) +
+            #~ FollowedBy(blockStatementExpr) +
+            INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT)
+    else:
+        smExpr = Group( Optional(NL) +
+            (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) )
+    blockStatementExpr.ignore(_bslash + LineEnd())
+    return smExpr.setName('indented block')
+
+alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]")
+punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]")
+
+anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag'))
+_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\''))
+commonHTMLEntity = Regex('&(?P<entity>' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity")
+def replaceHTMLEntity(t):
+    """Helper parser action to replace common HTML entities with their special characters"""
+    return _htmlEntityMap.get(t.entity)
+
+# it's easy to get these comment structures wrong - they're very common, so may as well make them available
+cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment")
+"Comment of the form C{/* ... */}"
+
+htmlComment = Regex(r"<!--[\s\S]*?-->").setName("HTML comment")
+"Comment of the form C{<!-- ... -->}"
+
+restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line")
+dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment")
+"Comment of the form C{// ... (to end of line)}"
+
+cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment")
+"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}"
+
+javaStyleComment = cppStyleComment
+"Same as C{L{cppStyleComment}}"
+
+pythonStyleComment = Regex(r"#.*").setName("Python style comment")
+"Comment of the form C{# ... (to end of line)}"
+
+_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') +
+                                  Optional( Word(" \t") +
+                                            ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem")
+commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList")
+"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas.
+   This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}."""
+
+# some other useful expressions - using lower-case class name since we are really using this as a namespace
+class pyparsing_common:
+    """
+    Here are some common low-level expressions that may be useful in jump-starting parser development:
+     - numeric forms (L{integers<integer>}, L{reals<real>}, L{scientific notation<sci_real>})
+     - common L{programming identifiers<identifier>}
+     - network addresses (L{MAC<mac_address>}, L{IPv4<ipv4_address>}, L{IPv6<ipv6_address>})
+     - ISO8601 L{dates<iso8601_date>} and L{datetime<iso8601_datetime>}
+     - L{UUID<uuid>}
+     - L{comma-separated list<comma_separated_list>}
+    Parse actions:
+     - C{L{convertToInteger}}
+     - C{L{convertToFloat}}
+     - C{L{convertToDate}}
+     - C{L{convertToDatetime}}
+     - C{L{stripHTMLTags}}
+     - C{L{upcaseTokens}}
+     - C{L{downcaseTokens}}
+
+    Example::
+        pyparsing_common.number.runTests('''
+            # any int or real number, returned as the appropriate type
+            100
+            -100
+            +100
+            3.14159
+            6.02e23
+            1e-12
+            ''')
+
+        pyparsing_common.fnumber.runTests('''
+            # any int or real number, returned as float
+            100
+            -100
+            +100
+            3.14159
+            6.02e23
+            1e-12
+            ''')
+
+        pyparsing_common.hex_integer.runTests('''
+            # hex numbers
+            100
+            FF
+            ''')
+
+        pyparsing_common.fraction.runTests('''
+            # fractions
+            1/2
+            -3/4
+            ''')
+
+        pyparsing_common.mixed_integer.runTests('''
+            # mixed fractions
+            1
+            1/2
+            -3/4
+            1-3/4
+            ''')
+
+        import uuid
+        pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID))
+        pyparsing_common.uuid.runTests('''
+            # uuid
+            12345678-1234-5678-1234-567812345678
+            ''')
+    prints::
+        # any int or real number, returned as the appropriate type
+        100
+        [100]
+
+        -100
+        [-100]
+
+        +100
+        [100]
+
+        3.14159
+        [3.14159]
+
+        6.02e23
+        [6.02e+23]
+
+        1e-12
+        [1e-12]
+
+        # any int or real number, returned as float
+        100
+        [100.0]
+
+        -100
+        [-100.0]
+
+        +100
+        [100.0]
+
+        3.14159
+        [3.14159]
+
+        6.02e23
+        [6.02e+23]
+
+        1e-12
+        [1e-12]
+
+        # hex numbers
+        100
+        [256]
+
+        FF
+        [255]
+
+        # fractions
+        1/2
+        [0.5]
+
+        -3/4
+        [-0.75]
+
+        # mixed fractions
+        1
+        [1]
+
+        1/2
+        [0.5]
+
+        -3/4
+        [-0.75]
+
+        1-3/4
+        [1.75]
+
+        # uuid
+        12345678-1234-5678-1234-567812345678
+        [UUID('12345678-1234-5678-1234-567812345678')]
+    """
+
+    convertToInteger = tokenMap(int)
+    """
+    Parse action for converting parsed integers to Python int
+    """
+
+    convertToFloat = tokenMap(float)
+    """
+    Parse action for converting parsed numbers to Python float
+    """
+
+    integer = Word(nums).setName("integer").setParseAction(convertToInteger)
+    """expression that parses an unsigned integer, returns an int"""
+
+    hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16))
+    """expression that parses a hexadecimal integer, returns an int"""
+
+    signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger)
+    """expression that parses an integer with optional leading sign, returns an int"""
+
+    fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction")
+    """fractional expression of an integer divided by an integer, returns a float"""
+    fraction.addParseAction(lambda t: t[0]/t[-1])
+
+    mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction")
+    """mixed integer of the form 'integer - fraction', with optional leading integer, returns float"""
+    mixed_integer.addParseAction(sum)
+
+    real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat)
+    """expression that parses a floating point number and returns a float"""
+
+    sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat)
+    """expression that parses a floating point number with optional scientific notation and returns a float"""
+
+    # streamlining this expression makes the docs nicer-looking
+    number = (sci_real | real | signed_integer).streamline()
+    """any numeric expression, returns the corresponding Python type"""
+
+    fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat)
+    """any int or real number, returned as float"""
+
+    identifier = Word(alphas+'_', alphanums+'_').setName("identifier")
+    """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')"""
+
+    ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address")
+    "IPv4 address (C{0.0.0.0 - 255.255.255.255})"
+
+    _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer")
+    _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address")
+    _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address")
+    _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8)
+    _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address")
+    ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address")
+    "IPv6 address (long, short, or mixed form)"
+
+    mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address")
+    "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)"
+
+    @staticmethod
+    def convertToDate(fmt="%Y-%m-%d"):
+        """
+        Helper to create a parse action for converting parsed date string to Python datetime.date
+
+        Params -
+         - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"})
+
+        Example::
+            date_expr = pyparsing_common.iso8601_date.copy()
+            date_expr.setParseAction(pyparsing_common.convertToDate())
+            print(date_expr.parseString("1999-12-31"))
+        prints::
+            [datetime.date(1999, 12, 31)]
+        """
+        def cvt_fn(s,l,t):
+            try:
+                return datetime.strptime(t[0], fmt).date()
+            except ValueError as ve:
+                raise ParseException(s, l, str(ve))
+        return cvt_fn
+
+    @staticmethod
+    def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"):
+        """
+        Helper to create a parse action for converting parsed datetime string to Python datetime.datetime
+
+        Params -
+         - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"})
+
+        Example::
+            dt_expr = pyparsing_common.iso8601_datetime.copy()
+            dt_expr.setParseAction(pyparsing_common.convertToDatetime())
+            print(dt_expr.parseString("1999-12-31T23:59:59.999"))
+        prints::
+            [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)]
+        """
+        def cvt_fn(s,l,t):
+            try:
+                return datetime.strptime(t[0], fmt)
+            except ValueError as ve:
+                raise ParseException(s, l, str(ve))
+        return cvt_fn
+
+    iso8601_date = Regex(r'(?P<year>\d{4})(?:-(?P<month>\d\d)(?:-(?P<day>\d\d))?)?').setName("ISO8601 date")
+    "ISO8601 date (C{yyyy-mm-dd})"
+
+    iso8601_datetime = Regex(r'(?P<year>\d{4})-(?P<month>\d\d)-(?P<day>\d\d)[T ](?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d(\.\d*)?)?)?(?P<tz>Z|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime")
+    "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}"
+
+    uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID")
+    "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})"
+
+    _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress()
+    @staticmethod
+    def stripHTMLTags(s, l, tokens):
+        """
+        Parse action to remove HTML tags from web page HTML source
+
+        Example::
+            # strip HTML links from normal text
+            text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>'
+            td,td_end = makeHTMLTags("TD")
+            table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end
+
+            print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page'
+        """
+        return pyparsing_common._html_stripper.transformString(tokens[0])
+
+    _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',')
+                                        + Optional( White(" \t") ) ) ).streamline().setName("commaItem")
+    comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list")
+    """Predefined expression of 1 or more printable words or quoted strings, separated by commas."""
+
+    upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper()))
+    """Parse action to convert tokens to upper case."""
+
+    downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower()))
+    """Parse action to convert tokens to lower case."""
+
+
+if __name__ == "__main__":
+
+    selectToken    = CaselessLiteral("select")
+    fromToken      = CaselessLiteral("from")
+
+    ident          = Word(alphas, alphanums + "_$")
+
+    columnName     = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens)
+    columnNameList = Group(delimitedList(columnName)).setName("columns")
+    columnSpec     = ('*' | columnNameList)
+
+    tableName      = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens)
+    tableNameList  = Group(delimitedList(tableName)).setName("tables")
+
+    simpleSQL      = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables")
+
+    # demo runTests method, including embedded comments in test string
+    simpleSQL.runTests("""
+        # '*' as column list and dotted table name
+        select * from SYS.XYZZY
+
+        # caseless match on "SELECT", and casts back to "select"
+        SELECT * from XYZZY, ABC
+
+        # list of column names, and mixed case SELECT keyword
+        Select AA,BB,CC from Sys.dual
+
+        # multiple tables
+        Select A, B, C from Sys.dual, Table2
+
+        # invalid SELECT keyword - should fail
+        Xelect A, B, C from Sys.dual
+
+        # incomplete command - should fail
+        Select
+
+        # invalid column name - should fail
+        Select ^^^ frox Sys.dual
+
+        """)
+
+    pyparsing_common.number.runTests("""
+        100
+        -100
+        +100
+        3.14159
+        6.02e23
+        1e-12
+        """)
+
+    # any int or real number, returned as float
+    pyparsing_common.fnumber.runTests("""
+        100
+        -100
+        +100
+        3.14159
+        6.02e23
+        1e-12
+        """)
+
+    pyparsing_common.hex_integer.runTests("""
+        100
+        FF
+        """)
+
+    import uuid
+    pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID))
+    pyparsing_common.uuid.runTests("""
+        12345678-1234-5678-1234-567812345678
+        """)
diff --git a/tools/ldgen/samples/esp32.lf b/tools/ldgen/samples/esp32.lf
new file mode 100644 (file)
index 0000000..03a9377
--- /dev/null
@@ -0,0 +1,80 @@
+[sections:text]
+entries: 
+    .text+
+    .literal+
+    
+[sections:data]
+entries:
+    .data+
+
+[sections:bss]
+entries:
+    .bss+
+
+[sections:common]
+entries:
+    COMMON
+
+[sections:rodata]
+entries:
+    .rodata+
+
+[sections:rtc_text]
+entries:
+    .rtc.text
+    .rtc.literal
+
+[sections:rtc_data]
+entries:
+    .rtc.data
+
+[sections:rtc_rodata]
+entries:
+    .rtc.rodata
+
+[sections:rtc_bss]
+entries:
+    .rtc.bss
+
+[sections:extram_bss]
+entries:
+    .exram.bss
+
+[sections:iram]
+entries:
+    .iram1+
+
+[sections:dram]
+entries:
+    .dram1+
+
+[scheme:default]
+entries:
+    text -> flash_text
+    rodata -> flash_rodata
+    data -> dram0_data
+    bss -> dram0_bss
+    common -> dram0_bss
+    iram -> iram0_text
+    dram -> dram0_data
+    rtc_text -> rtc_text
+    rtc_data -> rtc_data
+    rtc_rodata -> rtc_data
+    rtc_bss -> rtc_bss
+
+[scheme:rtc]
+entries:
+    text -> rtc_text
+    data -> rtc_data
+    rodata -> rtc_data
+    bss -> rtc_bss
+    common -> rtc_bss
+
+[scheme:noflash]
+entries:
+    text -> iram0_text
+    rodata -> dram0_data
+
+[scheme:noflash_data]
+entries:
+    rodata -> dram0_data
\ No newline at end of file
diff --git a/tools/ldgen/samples/mappings.lf b/tools/ldgen/samples/mappings.lf
new file mode 100644 (file)
index 0000000..bd029e8
--- /dev/null
@@ -0,0 +1,62 @@
+
+[mapping]
+archive: libheap.a
+entries:
+    multi_heap (noflash)
+    multi_heap_poisoning (noflash)
+
+[mapping]
+archive: libsoc.a
+entries: 
+    * (noflash)
+
+[mapping]
+archive: libfreertos.a
+entries:
+    * (noflash)
+
+[mapping]
+archive: libesp32.a
+entries: 
+    core_dump (noflash)
+    panic (noflash)
+
+[mapping]
+archive: libapp_trace.a
+entries: 
+    * (noflash)
+
+[mapping]
+archive: libxtensa-debug-module.a
+entries: 
+    eri (noflash)
+
+[mapping]
+archive: libphy.a
+entries: 
+    * (noflash_data)
+
+[mapping]
+archive: librtc.a
+entries: 
+    * (noflash)
+
+[mapping]
+archive: libhal.a
+entries: 
+    * (noflash)
+
+[mapping]
+archive: libgcc.a
+entries: 
+    lib2funcs (noflash)
+
+[mapping]
+archive: libspi_flash.a
+entries: 
+    spi_flash_rom_patch (noflash)
+
+[mapping]
+archive: libgcov.a
+entries: 
+    * (noflash)
\ No newline at end of file
diff --git a/tools/ldgen/samples/sdkconfig b/tools/ldgen/samples/sdkconfig
new file mode 100644 (file)
index 0000000..869e9bf
--- /dev/null
@@ -0,0 +1,542 @@
+#
+# SDK tool configuration
+#
+CONFIG_TOOLPREFIX="xtensa-esp32-elf-"
+CONFIG_PYTHON="python"
+CONFIG_MAKE_WARN_UNDEFINED_VARIABLES=y
+
+#
+# Bootloader config
+#
+CONFIG_LOG_BOOTLOADER_LEVEL_NONE=
+CONFIG_LOG_BOOTLOADER_LEVEL_ERROR=
+CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y
+CONFIG_LOG_BOOTLOADER_LEVEL_INFO=
+CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG=
+CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE=
+CONFIG_LOG_BOOTLOADER_LEVEL=2
+CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V=
+CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y
+
+#
+# Security features
+#
+CONFIG_SECURE_BOOT_ENABLED=
+CONFIG_FLASH_ENCRYPTION_ENABLED=
+
+#
+# Serial flasher config
+#
+CONFIG_ESPTOOLPY_PORT="/dev/ttyUSB0"
+CONFIG_ESPTOOLPY_BAUD_115200B=y
+CONFIG_ESPTOOLPY_BAUD_230400B=
+CONFIG_ESPTOOLPY_BAUD_921600B=
+CONFIG_ESPTOOLPY_BAUD_2MB=
+CONFIG_ESPTOOLPY_BAUD_OTHER=
+CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200
+CONFIG_ESPTOOLPY_BAUD=115200
+CONFIG_ESPTOOLPY_COMPRESSED=
+CONFIG_FLASHMODE_QIO=
+CONFIG_FLASHMODE_QOUT=
+CONFIG_FLASHMODE_DIO=y
+CONFIG_FLASHMODE_DOUT=
+CONFIG_ESPTOOLPY_FLASHMODE="dio"
+CONFIG_ESPTOOLPY_FLASHFREQ_80M=
+CONFIG_ESPTOOLPY_FLASHFREQ_40M=y
+CONFIG_ESPTOOLPY_FLASHFREQ_26M=
+CONFIG_ESPTOOLPY_FLASHFREQ_20M=
+CONFIG_ESPTOOLPY_FLASHFREQ="40m"
+CONFIG_ESPTOOLPY_FLASHSIZE_1MB=
+CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y
+CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
+CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
+CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
+CONFIG_ESPTOOLPY_FLASHSIZE="2MB"
+CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y
+CONFIG_ESPTOOLPY_BEFORE_RESET=y
+CONFIG_ESPTOOLPY_BEFORE_NORESET=
+CONFIG_ESPTOOLPY_BEFORE="default_reset"
+CONFIG_ESPTOOLPY_AFTER_RESET=y
+CONFIG_ESPTOOLPY_AFTER_NORESET=
+CONFIG_ESPTOOLPY_AFTER="hard_reset"
+CONFIG_MONITOR_BAUD_9600B=
+CONFIG_MONITOR_BAUD_57600B=
+CONFIG_MONITOR_BAUD_115200B=y
+CONFIG_MONITOR_BAUD_230400B=
+CONFIG_MONITOR_BAUD_921600B=
+CONFIG_MONITOR_BAUD_2MB=
+CONFIG_MONITOR_BAUD_OTHER=
+CONFIG_MONITOR_BAUD_OTHER_VAL=115200
+CONFIG_MONITOR_BAUD=115200
+
+#
+# Partition Table
+#
+CONFIG_PARTITION_TABLE_SINGLE_APP=y
+CONFIG_PARTITION_TABLE_TWO_OTA=
+CONFIG_PARTITION_TABLE_CUSTOM=
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
+CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000
+CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv"
+CONFIG_APP_OFFSET=0x10000
+CONFIG_PARTITION_TABLE_MD5=y
+
+#
+# Compiler options
+#
+CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
+CONFIG_OPTIMIZATION_LEVEL_RELEASE=
+CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
+CONFIG_OPTIMIZATION_ASSERTIONS_SILENT=
+CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED=
+CONFIG_CXX_EXCEPTIONS=
+CONFIG_STACK_CHECK_NONE=y
+CONFIG_STACK_CHECK_NORM=
+CONFIG_STACK_CHECK_STRONG=
+CONFIG_STACK_CHECK_ALL=
+CONFIG_STACK_CHECK=
+CONFIG_WARN_WRITE_STRINGS=
+
+#
+# Component config
+#
+
+#
+# Application Level Tracing
+#
+CONFIG_ESP32_APPTRACE_DEST_TRAX=
+CONFIG_ESP32_APPTRACE_DEST_NONE=y
+CONFIG_ESP32_APPTRACE_ENABLE=
+CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
+
+#
+# FreeRTOS SystemView Tracing
+#
+CONFIG_AWS_IOT_SDK=
+
+#
+# Bluetooth
+#
+CONFIG_BT_ENABLED=
+CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0
+CONFIG_BT_RESERVE_DRAM=0
+
+#
+# ADC configuration
+#
+CONFIG_ADC_FORCE_XPD_FSM=
+CONFIG_ADC2_DISABLE_DAC=y
+
+#
+# ESP32-specific
+#
+CONFIG_ESP32_DEFAULT_CPU_FREQ_80=
+CONFIG_ESP32_DEFAULT_CPU_FREQ_160=
+CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
+CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
+CONFIG_SPIRAM_SUPPORT=
+CONFIG_MEMMAP_TRACEMEM=
+CONFIG_MEMMAP_TRACEMEM_TWOBANKS=
+CONFIG_ESP32_TRAX=
+CONFIG_TRACEMEM_RESERVE_DRAM=0x0
+CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH=
+CONFIG_ESP32_ENABLE_COREDUMP_TO_UART=
+CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y
+CONFIG_ESP32_ENABLE_COREDUMP=
+CONFIG_TWO_UNIVERSAL_MAC_ADDRESS=
+CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y
+CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4
+CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
+CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048
+CONFIG_MAIN_TASK_STACK_SIZE=4096
+CONFIG_IPC_TASK_STACK_SIZE=1024
+CONFIG_TIMER_TASK_STACK_SIZE=3584
+CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y
+CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF=
+CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR=
+CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF=
+CONFIG_NEWLIB_STDIN_LINE_ENDING_LF=
+CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y
+CONFIG_NEWLIB_NANO_FORMAT=
+CONFIG_CONSOLE_UART_DEFAULT=y
+CONFIG_CONSOLE_UART_CUSTOM=
+CONFIG_CONSOLE_UART_NONE=
+CONFIG_CONSOLE_UART_NUM=0
+CONFIG_CONSOLE_UART_BAUDRATE=115200
+CONFIG_ULP_COPROC_ENABLED=
+CONFIG_ULP_COPROC_RESERVE_MEM=0
+CONFIG_ESP32_PANIC_PRINT_HALT=
+CONFIG_ESP32_PANIC_PRINT_REBOOT=y
+CONFIG_ESP32_PANIC_SILENT_REBOOT=
+CONFIG_ESP32_PANIC_GDBSTUB=
+CONFIG_ESP32_DEBUG_OCDAWARE=y
+CONFIG_INT_WDT=y
+CONFIG_INT_WDT_TIMEOUT_MS=300
+CONFIG_TASK_WDT=y
+CONFIG_TASK_WDT_PANIC=
+CONFIG_TASK_WDT_TIMEOUT_S=5
+CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
+CONFIG_BROWNOUT_DET=y
+CONFIG_BROWNOUT_DET_LVL_SEL_0=y
+CONFIG_BROWNOUT_DET_LVL_SEL_1=
+CONFIG_BROWNOUT_DET_LVL_SEL_2=
+CONFIG_BROWNOUT_DET_LVL_SEL_3=
+CONFIG_BROWNOUT_DET_LVL_SEL_4=
+CONFIG_BROWNOUT_DET_LVL_SEL_5=
+CONFIG_BROWNOUT_DET_LVL_SEL_6=
+CONFIG_BROWNOUT_DET_LVL_SEL_7=
+CONFIG_BROWNOUT_DET_LVL=0
+CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y
+CONFIG_ESP32_TIME_SYSCALL_USE_RTC=
+CONFIG_ESP32_TIME_SYSCALL_USE_FRC1=
+CONFIG_ESP32_TIME_SYSCALL_USE_NONE=
+CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y
+CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL=
+CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024
+CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES=100
+CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000
+CONFIG_ESP32_XTAL_FREQ_40=y
+CONFIG_ESP32_XTAL_FREQ_26=
+CONFIG_ESP32_XTAL_FREQ_AUTO=
+CONFIG_ESP32_XTAL_FREQ=40
+CONFIG_DISABLE_BASIC_ROM_CONSOLE=
+CONFIG_NO_BLOBS=
+CONFIG_ESP_TIMER_PROFILING=
+CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS=
+CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
+
+#
+# Wi-Fi
+#
+CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10
+CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32
+CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=
+CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y
+CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1
+CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32
+CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
+CONFIG_ESP32_WIFI_TX_BA_WIN=6
+CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
+CONFIG_ESP32_WIFI_RX_BA_WIN=6
+CONFIG_ESP32_WIFI_NVS_ENABLED=y
+
+#
+# PHY
+#
+CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y
+CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION=
+CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20
+CONFIG_ESP32_PHY_MAX_TX_POWER=20
+
+#
+# Power Management
+#
+CONFIG_PM_ENABLE=
+
+#
+# ADC-Calibration
+#
+CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y
+CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y
+CONFIG_ADC_CAL_LUT_ENABLE=y
+
+#
+# Ethernet
+#
+CONFIG_DMA_RX_BUF_NUM=10
+CONFIG_DMA_TX_BUF_NUM=10
+CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE=
+CONFIG_EMAC_TASK_PRIORITY=20
+
+#
+# FAT Filesystem support
+#
+CONFIG_FATFS_CODEPAGE_DYNAMIC=
+CONFIG_FATFS_CODEPAGE_437=y
+CONFIG_FATFS_CODEPAGE_720=
+CONFIG_FATFS_CODEPAGE_737=
+CONFIG_FATFS_CODEPAGE_771=
+CONFIG_FATFS_CODEPAGE_775=
+CONFIG_FATFS_CODEPAGE_850=
+CONFIG_FATFS_CODEPAGE_852=
+CONFIG_FATFS_CODEPAGE_855=
+CONFIG_FATFS_CODEPAGE_857=
+CONFIG_FATFS_CODEPAGE_860=
+CONFIG_FATFS_CODEPAGE_861=
+CONFIG_FATFS_CODEPAGE_862=
+CONFIG_FATFS_CODEPAGE_863=
+CONFIG_FATFS_CODEPAGE_864=
+CONFIG_FATFS_CODEPAGE_865=
+CONFIG_FATFS_CODEPAGE_866=
+CONFIG_FATFS_CODEPAGE_869=
+CONFIG_FATFS_CODEPAGE_932=
+CONFIG_FATFS_CODEPAGE_936=
+CONFIG_FATFS_CODEPAGE_949=
+CONFIG_FATFS_CODEPAGE_950=
+CONFIG_FATFS_CODEPAGE=437
+CONFIG_FATFS_LFN_NONE=y
+CONFIG_FATFS_LFN_HEAP=
+CONFIG_FATFS_LFN_STACK=
+CONFIG_FATFS_FS_LOCK=0
+CONFIG_FATFS_TIMEOUT_MS=10000
+CONFIG_FATFS_PER_FILE_CACHE=y
+
+#
+# FreeRTOS
+#
+CONFIG_FREERTOS_UNICORE=y
+CONFIG_FREERTOS_CORETIMER_0=y
+CONFIG_FREERTOS_CORETIMER_1=
+CONFIG_FREERTOS_HZ=1000
+CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y
+CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE=
+CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL=y
+CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=
+CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=
+CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y
+CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3
+CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y
+CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE=
+CONFIG_FREERTOS_ASSERT_DISABLE=
+CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1024
+CONFIG_FREERTOS_ISR_STACKSIZE=1536
+CONFIG_FREERTOS_LEGACY_HOOKS=
+CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
+CONFIG_SUPPORT_STATIC_ALLOCATION=
+CONFIG_TIMER_TASK_PRIORITY=1
+CONFIG_TIMER_TASK_STACK_DEPTH=2048
+CONFIG_TIMER_QUEUE_LENGTH=10
+CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
+CONFIG_FREERTOS_USE_TRACE_FACILITY=
+CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=
+CONFIG_FREERTOS_DEBUG_INTERNALS=
+
+#
+# Heap memory debugging
+#
+CONFIG_HEAP_POISONING_DISABLED=y
+CONFIG_HEAP_POISONING_LIGHT=
+CONFIG_HEAP_POISONING_COMPREHENSIVE=
+CONFIG_HEAP_TRACING=
+
+#
+# libsodium
+#
+CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y
+
+#
+# Log output
+#
+CONFIG_LOG_DEFAULT_LEVEL_NONE=
+CONFIG_LOG_DEFAULT_LEVEL_ERROR=
+CONFIG_LOG_DEFAULT_LEVEL_WARN=
+CONFIG_LOG_DEFAULT_LEVEL_INFO=y
+CONFIG_LOG_DEFAULT_LEVEL_DEBUG=
+CONFIG_LOG_DEFAULT_LEVEL_VERBOSE=
+CONFIG_LOG_DEFAULT_LEVEL=3
+CONFIG_LOG_COLORS=y
+
+#
+# LWIP
+#
+CONFIG_L2_TO_L3_COPY=
+CONFIG_LWIP_IRAM_OPTIMIZATION=
+CONFIG_LWIP_MAX_SOCKETS=4
+CONFIG_LWIP_SO_REUSE=
+CONFIG_LWIP_SO_RCVBUF=
+CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1
+CONFIG_LWIP_IP_FRAG=
+CONFIG_LWIP_IP_REASSEMBLY=
+CONFIG_LWIP_STATS=
+CONFIG_LWIP_ETHARP_TRUST_IP_MAC=y
+CONFIG_TCPIP_RECVMBOX_SIZE=32
+CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y
+
+#
+# DHCP server
+#
+CONFIG_LWIP_DHCPS_LEASE_UNIT=60
+CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8
+CONFIG_LWIP_AUTOIP=
+CONFIG_LWIP_NETIF_LOOPBACK=y
+CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8
+
+#
+# TCP
+#
+CONFIG_LWIP_MAX_ACTIVE_TCP=16
+CONFIG_LWIP_MAX_LISTENING_TCP=16
+CONFIG_TCP_MAXRTX=12
+CONFIG_TCP_SYNMAXRTX=6
+CONFIG_TCP_MSS=1436
+CONFIG_TCP_MSL=60000
+CONFIG_TCP_SND_BUF_DEFAULT=5744
+CONFIG_TCP_WND_DEFAULT=5744
+CONFIG_TCP_RECVMBOX_SIZE=6
+CONFIG_TCP_QUEUE_OOSEQ=y
+CONFIG_TCP_OVERSIZE_MSS=y
+CONFIG_TCP_OVERSIZE_QUARTER_MSS=
+CONFIG_TCP_OVERSIZE_DISABLE=
+
+#
+# UDP
+#
+CONFIG_LWIP_MAX_UDP_PCBS=16
+CONFIG_UDP_RECVMBOX_SIZE=6
+CONFIG_TCPIP_TASK_STACK_SIZE=2048
+CONFIG_PPP_SUPPORT=
+
+#
+# ICMP
+#
+CONFIG_LWIP_MULTICAST_PING=
+CONFIG_LWIP_BROADCAST_PING=
+
+#
+# LWIP RAW API
+#
+CONFIG_LWIP_MAX_RAW_PCBS=16
+
+#
+# mbedTLS
+#
+CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384
+CONFIG_MBEDTLS_DEBUG=
+CONFIG_MBEDTLS_HARDWARE_AES=y
+CONFIG_MBEDTLS_HARDWARE_MPI=y
+CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y
+CONFIG_MBEDTLS_HARDWARE_SHA=
+CONFIG_MBEDTLS_HAVE_TIME=y
+CONFIG_MBEDTLS_HAVE_TIME_DATE=
+CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y
+CONFIG_MBEDTLS_TLS_SERVER_ONLY=
+CONFIG_MBEDTLS_TLS_CLIENT_ONLY=
+CONFIG_MBEDTLS_TLS_DISABLED=
+CONFIG_MBEDTLS_TLS_SERVER=y
+CONFIG_MBEDTLS_TLS_CLIENT=y
+CONFIG_MBEDTLS_TLS_ENABLED=y
+
+#
+# TLS Key Exchange Methods
+#
+CONFIG_MBEDTLS_PSK_MODES=
+CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y
+CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y
+CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y
+CONFIG_MBEDTLS_SSL_RENEGOTIATION=y
+CONFIG_MBEDTLS_SSL_PROTO_SSL3=
+CONFIG_MBEDTLS_SSL_PROTO_TLS1=y
+CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y
+CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y
+CONFIG_MBEDTLS_SSL_PROTO_DTLS=
+CONFIG_MBEDTLS_SSL_ALPN=y
+CONFIG_MBEDTLS_SSL_SESSION_TICKETS=y
+
+#
+# Symmetric Ciphers
+#
+CONFIG_MBEDTLS_AES_C=y
+CONFIG_MBEDTLS_CAMELLIA_C=
+CONFIG_MBEDTLS_DES_C=
+CONFIG_MBEDTLS_RC4_DISABLED=y
+CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT=
+CONFIG_MBEDTLS_RC4_ENABLED=
+CONFIG_MBEDTLS_BLOWFISH_C=
+CONFIG_MBEDTLS_XTEA_C=
+CONFIG_MBEDTLS_CCM_C=y
+CONFIG_MBEDTLS_GCM_C=y
+CONFIG_MBEDTLS_RIPEMD160_C=
+
+#
+# Certificates
+#
+CONFIG_MBEDTLS_PEM_PARSE_C=y
+CONFIG_MBEDTLS_PEM_WRITE_C=y
+CONFIG_MBEDTLS_X509_CRL_PARSE_C=y
+CONFIG_MBEDTLS_X509_CSR_PARSE_C=y
+CONFIG_MBEDTLS_ECP_C=y
+CONFIG_MBEDTLS_ECDH_C=y
+CONFIG_MBEDTLS_ECDSA_C=y
+CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y
+CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y
+CONFIG_MBEDTLS_ECP_NIST_OPTIM=y
+
+#
+# OpenSSL
+#
+CONFIG_OPENSSL_DEBUG=
+CONFIG_OPENSSL_ASSERT_DO_NOTHING=y
+CONFIG_OPENSSL_ASSERT_EXIT=
+
+#
+# PThreads
+#
+CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5
+CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072
+
+#
+# SPI Flash driver
+#
+CONFIG_SPI_FLASH_VERIFY_WRITE=
+CONFIG_SPI_FLASH_ENABLE_COUNTERS=
+CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y
+CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y
+CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS=
+CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED=
+
+#
+# SPIFFS Configuration
+#
+CONFIG_SPIFFS_MAX_PARTITIONS=3
+
+#
+# SPIFFS Cache Configuration
+#
+CONFIG_SPIFFS_CACHE=y
+CONFIG_SPIFFS_CACHE_WR=y
+CONFIG_SPIFFS_CACHE_STATS=
+CONFIG_SPIFFS_PAGE_CHECK=y
+CONFIG_SPIFFS_GC_MAX_RUNS=10
+CONFIG_SPIFFS_GC_STATS=
+CONFIG_SPIFFS_PAGE_SIZE=256
+CONFIG_SPIFFS_OBJ_NAME_LEN=32
+CONFIG_SPIFFS_USE_MAGIC=y
+CONFIG_SPIFFS_USE_MAGIC_LENGTH=y
+CONFIG_SPIFFS_META_LENGTH=4
+CONFIG_SPIFFS_USE_MTIME=y
+
+#
+# Debug Configuration
+#
+CONFIG_SPIFFS_DBG=
+CONFIG_SPIFFS_API_DBG=
+CONFIG_SPIFFS_GC_DBG=
+CONFIG_SPIFFS_CACHE_DBG=
+CONFIG_SPIFFS_CHECK_DBG=
+CONFIG_SPIFFS_TEST_VISUALISATION=
+
+#
+# tcpip adapter
+#
+CONFIG_IP_LOST_TIMER_INTERVAL=120
+
+#
+# Wear Levelling
+#
+CONFIG_WL_SECTOR_SIZE_512=
+CONFIG_WL_SECTOR_SIZE_4096=y
+CONFIG_WL_SECTOR_SIZE=4096
diff --git a/tools/ldgen/samples/sections.info b/tools/ldgen/samples/sections.info
new file mode 100644 (file)
index 0000000..db450cf
--- /dev/null
@@ -0,0 +1,1626 @@
+In archive libfreertos.a:
+
+FreeRTOS-openocd.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .text         00000000  00000000  00000000  00000034  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  1 .data         00000000  00000000  00000000  00000034  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  2 .bss          00000000  00000000  00000000  00000034  2**0
+                  ALLOC
+  3 .dram1        00000004  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+  4 .debug_info   0000008f  00000000  00000000  00000038  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  5 .debug_abbrev 0000003e  00000000  00000000  000000c7  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  6 .debug_aranges 00000018  00000000  00000000  00000105  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  7 .debug_line   0000005e  00000000  00000000  0000011d  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  8 .debug_str    0000018d  00000000  00000000  0000017b  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  9 .comment      0000003b  00000000  00000000  00000308  2**0
+                  CONTENTS, READONLY
+ 10 .xtensa.info  00000038  00000000  00000000  00000343  2**0
+                  CONTENTS, READONLY
+ 11 .xt.prop      0000000c  00000000  00000000  0000037b  2**0
+                  CONTENTS, RELOC, READONLY
+
+croutine.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.prvCheckPendingReadyList 00000018  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.prvCheckDelayedList 0000002c  00000000  00000000  0000004c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.xCoRoutineCreate 0000001c  00000000  00000000  00000078  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.vCoRoutineAddToDelayedList 00000020  00000000  00000000  00000094  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.vCoRoutineSchedule 00000014  00000000  00000000  000000b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.xCoRoutineRemoveFromEventList 00000010  00000000  00000000  000000c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .text         00000000  00000000  00000000  000000d8  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  7 .data         00000000  00000000  00000000  000000d8  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  8 .bss          00000000  00000000  00000000  000000d8  2**0
+                  ALLOC
+  9 .text.prvCheckPendingReadyList 00000056  00000000  00000000  000000d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .text.prvCheckDelayedList 000000ac  00000000  00000000  00000130  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .rodata.str1.4 00000074  00000000  00000000  000001dc  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 12 .text.xCoRoutineCreate 00000028  00000000  00000000  00000250  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .text.vCoRoutineAddToDelayedList 00000056  00000000  00000000  00000278  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .text.vCoRoutineSchedule 0000007a  00000000  00000000  000002d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 15 .text.xCoRoutineRemoveFromEventList 00000031  00000000  00000000  0000034c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .rodata.__FUNCTION__$5025 00000011  00000000  00000000  00000380  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 17 .bss.xPassedTicks 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 18 .bss.xLastTickCount 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 19 .bss.xCoRoutineTickCount 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 20 .bss.uxTopCoRoutineReadyPriority 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 21 .bss.pxCurrentCoRoutine 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 22 .bss.xPendingReadyCoRoutineList 00000014  00000000  00000000  00000394  2**2
+                  ALLOC
+ 23 .bss.pxOverflowDelayedCoRoutineList 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 24 .bss.pxDelayedCoRoutineList 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 25 .bss.pxReadyCoRoutineLists 00000028  00000000  00000000  00000394  2**2
+                  ALLOC
+ 26 .debug_frame  000000a0  00000000  00000000  00000394  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 27 .debug_info   000006b8  00000000  00000000  00000434  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 28 .debug_abbrev 00000233  00000000  00000000  00000aec  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 29 .debug_loc    0000013b  00000000  00000000  00000d1f  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 30 .debug_aranges 00000048  00000000  00000000  00000e5a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 31 .debug_ranges 00000038  00000000  00000000  00000ea2  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 32 .debug_line   0000037e  00000000  00000000  00000eda  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 33 .debug_str    00000533  00000000  00000000  00001258  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 34 .comment      0000003b  00000000  00000000  0000178b  2**0
+                  CONTENTS, READONLY
+ 35 .xtensa.info  00000038  00000000  00000000  000017c6  2**0
+                  CONTENTS, READONLY
+ 36 .xt.lit       00000030  00000000  00000000  000017fe  2**0
+                  CONTENTS, RELOC, READONLY
+ 37 .xt.prop      00000270  00000000  00000000  0000182e  2**0
+                  CONTENTS, RELOC, READONLY
+
+event_groups.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.xEventGroupCreate 0000000c  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.xEventGroupWaitBits 0000006c  00000000  00000000  00000040  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.xEventGroupClearBits 00000028  00000000  00000000  000000ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.xEventGroupGetBitsFromISR 00000004  00000000  00000000  000000d4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.xEventGroupSetBits 0000003c  00000000  00000000  000000d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.xEventGroupSync 0000005c  00000000  00000000  00000114  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .literal.vEventGroupDelete 00000030  00000000  00000000  00000170  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.vEventGroupSetBitsCallback 00000004  00000000  00000000  000001a0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.vEventGroupClearBitsCallback 00000004  00000000  00000000  000001a4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .text         00000000  00000000  00000000  000001a8  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 10 .data         00000000  00000000  00000000  000001a8  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 11 .bss          00000000  00000000  00000000  000001a8  2**0
+                  ALLOC
+ 12 .text.prvTestWaitCondition 0000001e  00000000  00000000  000001a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .text.xEventGroupCreate 00000027  00000000  00000000  000001c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .rodata.str1.4 00000058  00000000  00000000  000001f0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 15 .text.xEventGroupWaitBits 00000140  00000000  00000000  00000248  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .text.xEventGroupClearBits 00000060  00000000  00000000  00000388  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .text.xEventGroupGetBitsFromISR 00000010  00000000  00000000  000003e8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .text.xEventGroupSetBits 000000c3  00000000  00000000  000003f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .text.xEventGroupSync 00000108  00000000  00000000  000004bc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .text.vEventGroupDelete 0000005d  00000000  00000000  000005c4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .text.vEventGroupSetBitsCallback 0000000f  00000000  00000000  00000624  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 22 .text.vEventGroupClearBitsCallback 0000000f  00000000  00000000  00000634  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .rodata.__FUNCTION__$5129 00000012  00000000  00000000  00000644  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 24 .rodata.__FUNCTION__$5120 00000013  00000000  00000000  00000658  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 25 .rodata.__FUNCTION__$5100 00000015  00000000  00000000  0000066c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 26 .rodata.__FUNCTION__$5092 00000014  00000000  00000000  00000684  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 27 .rodata.__FUNCTION__$5078 00000010  00000000  00000000  00000698  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 28 .debug_frame  00000100  00000000  00000000  000006a8  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 29 .debug_info   00000e02  00000000  00000000  000007a8  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 30 .debug_abbrev 00000236  00000000  00000000  000015aa  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 31 .debug_loc    00000546  00000000  00000000  000017e0  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 32 .debug_aranges 00000068  00000000  00000000  00001d26  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 33 .debug_ranges 00000070  00000000  00000000  00001d8e  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 34 .debug_line   000006af  00000000  00000000  00001dfe  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 35 .debug_str    00000677  00000000  00000000  000024ad  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 36 .comment      0000003b  00000000  00000000  00002b24  2**0
+                  CONTENTS, READONLY
+ 37 .xtensa.info  00000038  00000000  00000000  00002b5f  2**0
+                  CONTENTS, READONLY
+ 38 .xt.lit       00000048  00000000  00000000  00002b97  2**0
+                  CONTENTS, RELOC, READONLY
+ 39 .xt.prop      00000468  00000000  00000000  00002bdf  2**0
+                  CONTENTS, RELOC, READONLY
+
+list.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .text         00000000  00000000  00000000  00000034  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  1 .data         00000000  00000000  00000000  00000034  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  2 .bss          00000000  00000000  00000000  00000034  2**0
+                  ALLOC
+  3 .text.vListInitialise 00000015  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  4 .text.vListInitialiseItem 00000009  00000000  00000000  0000004c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  5 .text.vListInsertEnd 0000001b  00000000  00000000  00000058  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  6 .text.vListInsert 0000002f  00000000  00000000  00000074  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .text.uxListRemove 00000026  00000000  00000000  000000a4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .debug_frame  00000088  00000000  00000000  000000cc  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  9 .debug_info   000002a1  00000000  00000000  00000154  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 10 .debug_abbrev 000000dc  00000000  00000000  000003f5  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 11 .debug_loc    00000081  00000000  00000000  000004d1  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 12 .debug_aranges 00000040  00000000  00000000  00000552  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 13 .debug_ranges 00000030  00000000  00000000  00000592  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 14 .debug_line   0000024c  00000000  00000000  000005c2  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 15 .debug_str    000002c2  00000000  00000000  0000080e  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 16 .comment      0000003b  00000000  00000000  00000ad0  2**0
+                  CONTENTS, READONLY
+ 17 .xtensa.info  00000038  00000000  00000000  00000b0b  2**0
+                  CONTENTS, READONLY
+ 18 .xt.prop      000000f0  00000000  00000000  00000b43  2**0
+                  CONTENTS, RELOC, READONLY
+
+port.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.pxPortInitialiseStack 00000018  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.xPortStartScheduler 00000014  00000000  00000000  0000004c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.xPortSysTickHandler 00000008  00000000  00000000  00000060  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.vPortYieldOtherCore 00000004  00000000  00000000  00000068  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.vPortReleaseTaskMPUSettings 00000004  00000000  00000000  0000006c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.xPortInIsrContext 00000008  00000000  00000000  00000070  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .iram1.literal 00000004  00000000  00000000  00000078  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.vPortAssertIfInISR 00000018  00000000  00000000  0000007c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.vPortCPUInitializeMutex 00000004  00000000  00000000  00000094  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  9 .literal.vPortCPUAcquireMutex 00000030  00000000  00000000  00000098  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .literal.vPortCPUAcquireMutexTimeout 00000030  00000000  00000000  000000c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .literal.vPortCPUReleaseMutex 00000028  00000000  00000000  000000f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 12 .literal.vPortSetStackWatchpoint 00000008  00000000  00000000  00000120  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .text         00000000  00000000  00000000  00000128  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 14 .data         00000000  00000000  00000000  00000128  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 15 .bss          00000000  00000000  00000000  00000128  2**0
+                  ALLOC
+ 16 .text.pxPortInitialiseStack 00000086  00000000  00000000  00000128  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .text.vPortEndScheduler 00000005  00000000  00000000  000001b0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 18 .text.xPortStartScheduler 0000002e  00000000  00000000  000001b8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .text.xPortSysTickHandler 00000016  00000000  00000000  000001e8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .text.vPortYieldOtherCore 0000000e  00000000  00000000  00000200  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .text.vPortStoreTaskMPUSettings 00000013  00000000  00000000  00000210  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 22 .text.vPortReleaseTaskMPUSettings 0000000e  00000000  00000000  00000224  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .text.xPortInIsrContext 00000026  00000000  00000000  00000234  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .iram1        0000001a  00000000  00000000  0000025c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 25 .rodata.str1.4 0000013b  00000000  00000000  00000278  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 26 .text.vPortAssertIfInISR 00000025  00000000  00000000  000003b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 27 .text.vPortCPUInitializeMutex 0000000e  00000000  00000000  000003dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 28 .text.vPortCPUAcquireMutex 00000088  00000000  00000000  000003ec  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 29 .text.vPortCPUAcquireMutexTimeout 000000ab  00000000  00000000  00000474  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 30 .text.vPortCPUReleaseMutex 00000061  00000000  00000000  00000520  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 31 .text.vPortSetStackWatchpoint 0000001a  00000000  00000000  00000584  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 32 .text.xPortGetTickRateHz 00000008  00000000  00000000  000005a0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 33 .rodata.__func__$5264 00000029  00000000  00000000  000005a8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 34 .rodata.__func__$5259 00000029  00000000  00000000  000005d4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 35 .rodata.__FUNCTION__$5243 00000013  00000000  00000000  00000600  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 36 .bss.port_interruptNesting 00000008  00000000  00000000  00000614  2**2
+                  ALLOC
+ 37 .bss.port_xSchedulerRunning 00000008  00000000  00000000  00000614  2**2
+                  ALLOC
+ 38 .debug_frame  00000190  00000000  00000000  00000614  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 39 .debug_info   00000e78  00000000  00000000  000007a4  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 40 .debug_abbrev 00000404  00000000  00000000  0000161c  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 41 .debug_loc    000005f1  00000000  00000000  00001a20  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 42 .debug_aranges 00000098  00000000  00000000  00002011  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 43 .debug_ranges 000000a0  00000000  00000000  000020a9  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 44 .debug_line   000005fb  00000000  00000000  00002149  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 45 .debug_str    0000071f  00000000  00000000  00002744  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 46 .comment      0000003b  00000000  00000000  00002e63  2**0
+                  CONTENTS, READONLY
+ 47 .xtensa.info  00000038  00000000  00000000  00002e9e  2**0
+                  CONTENTS, READONLY
+ 48 .xt.lit       00000068  00000000  00000000  00002ed6  2**0
+                  CONTENTS, RELOC, READONLY
+ 49 .xt.prop      00000408  00000000  00000000  00002f3e  2**0
+                  CONTENTS, RELOC, READONLY
+
+portasm.S.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal      00000074  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .text         000001e0  00000000  00000000  000000a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .data         00000c0c  00000000  00000000  00000290  2**4
+                  CONTENTS, ALLOC, LOAD, DATA
+  3 .bss          00000000  00000000  00000000  00000e9c  2**0
+                  ALLOC
+  4 .xtensa.info  00000038  00000000  00000000  00000e9c  2**0
+                  CONTENTS, READONLY
+  5 .debug_line   00000432  00000000  00000000  00000ed4  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  6 .debug_info   00000093  00000000  00000000  00001306  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  7 .debug_abbrev 00000014  00000000  00000000  00001399  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  8 .debug_aranges 00000020  00000000  00000000  000013b0  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  9 .xt.lit       00000008  00000000  00000000  000013d0  2**0
+                  CONTENTS, RELOC, READONLY
+ 10 .xt.prop      00000168  00000000  00000000  000013d8  2**0
+                  CONTENTS, RELOC, READONLY
+
+queue.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.prvIsQueueFull 00000008  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.prvCopyDataToQueue 0000000c  00000000  00000000  0000003c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.prvNotifyQueueSetContainer 0000002c  00000000  00000000  00000048  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.prvCopyDataFromQueue 00000004  00000000  00000000  00000074  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.xQueueGenericReset 00000030  00000000  00000000  00000078  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.prvInitialiseNewQueue 00000004  00000000  00000000  000000a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .literal.xQueueGenericCreate 0000001c  00000000  00000000  000000ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.xQueueGetMutexHolder 00000008  00000000  00000000  000000c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.xQueueCreateCountingSemaphore 00000028  00000000  00000000  000000d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .literal.xQueueGenericSend 0000007c  00000000  00000000  000000f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .literal.prvInitialiseMutex 00000008  00000000  00000000  00000174  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .literal.xQueueCreateMutex 00000008  00000000  00000000  0000017c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 12 .literal.xQueueGiveMutexRecursive 0000001c  00000000  00000000  00000184  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .literal.xQueueGenericSendFromISR 0000003c  00000000  00000000  000001a0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .literal.xQueueGiveFromISR 00000030  00000000  00000000  000001dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 15 .literal.xQueueGenericReceive 00000078  00000000  00000000  0000020c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .literal.xQueueTakeMutexRecursive 0000001c  00000000  00000000  00000284  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .literal.xQueueReceiveFromISR 00000030  00000000  00000000  000002a0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .literal.xQueuePeekFromISR 00000034  00000000  00000000  000002d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .literal.uxQueueMessagesWaiting 0000001c  00000000  00000000  00000304  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .literal.uxQueueSpacesAvailable 0000001c  00000000  00000000  00000320  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .literal.uxQueueMessagesWaitingFromISR 0000001c  00000000  00000000  0000033c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 22 .literal.vQueueDelete 00000018  00000000  00000000  00000358  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .literal.xQueueIsQueueEmptyFromISR 0000001c  00000000  00000000  00000370  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .literal.xQueueIsQueueFullFromISR 0000001c  00000000  00000000  0000038c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 25 .literal.vQueueWaitForMessageRestricted 0000000c  00000000  00000000  000003a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 26 .literal.xQueueCreateSet 00000004  00000000  00000000  000003b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 27 .literal.xQueueSelectFromSet 00000004  00000000  00000000  000003b8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 28 .literal.xQueueSelectFromSetFromISR 00000004  00000000  00000000  000003bc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 29 .text         00000000  00000000  00000000  000003c0  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 30 .data         00000000  00000000  00000000  000003c0  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 31 .bss          00000000  00000000  00000000  000003c0  2**0
+                  ALLOC
+ 32 .text.prvIsQueueEmpty 00000012  00000000  00000000  000003c0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 33 .text.prvIsQueueFull 0000002a  00000000  00000000  000003d4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 34 .text.prvCopyDataToQueue 0000009e  00000000  00000000  00000400  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 35 .rodata.str1.4 00000050  00000000  00000000  000004a0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 36 .text.prvNotifyQueueSetContainer 00000076  00000000  00000000  000004f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 37 .text.prvCopyDataFromQueue 00000024  00000000  00000000  00000568  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 38 .text.xQueueGenericReset 00000096  00000000  00000000  0000058c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 39 .text.prvInitialiseNewQueue 00000023  00000000  00000000  00000624  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 40 .text.xQueueGenericCreate 0000004d  00000000  00000000  00000648  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 41 .text.xQueueGetMutexHolder 00000023  00000000  00000000  00000698  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 42 .text.xQueueCreateCountingSemaphore 0000006a  00000000  00000000  000006bc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 43 .text.xQueueGenericSend 0000018c  00000000  00000000  00000728  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 44 .text.prvInitialiseMutex 00000026  00000000  00000000  000008b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 45 .text.xQueueCreateMutex 0000001a  00000000  00000000  000008dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 46 .text.xQueueGiveMutexRecursive 0000004c  00000000  00000000  000008f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 47 .text.xQueueGenericSendFromISR 000000e6  00000000  00000000  00000944  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 48 .text.xQueueGiveFromISR 000000c2  00000000  00000000  00000a2c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 49 .text.xQueueGenericReceive 00000178  00000000  00000000  00000af0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 50 .text.xQueueTakeMutexRecursive 00000051  00000000  00000000  00000c68  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 51 .text.xQueueReceiveFromISR 000000a6  00000000  00000000  00000cbc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 52 .text.xQueuePeekFromISR 00000098  00000000  00000000  00000d64  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 53 .text.uxQueueMessagesWaiting 00000038  00000000  00000000  00000dfc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 54 .text.uxQueueSpacesAvailable 0000003e  00000000  00000000  00000e34  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 55 .text.uxQueueMessagesWaitingFromISR 00000038  00000000  00000000  00000e74  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 56 .text.vQueueDelete 00000028  00000000  00000000  00000eac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 57 .text.xQueueIsQueueEmptyFromISR 00000042  00000000  00000000  00000ed4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 58 .text.xQueueIsQueueFullFromISR 00000044  00000000  00000000  00000f18  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 59 .text.vQueueWaitForMessageRestricted 0000002a  00000000  00000000  00000f5c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 60 .text.xQueueCreateSet 00000014  00000000  00000000  00000f88  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 61 .text.xQueueAddToSet 0000001e  00000000  00000000  00000f9c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 62 .text.xQueueRemoveFromSet 00000020  00000000  00000000  00000fbc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 63 .text.xQueueSelectFromSet 00000018  00000000  00000000  00000fdc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 64 .text.xQueueSelectFromSetFromISR 00000015  00000000  00000000  00000ff4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 65 .rodata.__FUNCTION__$5459 00000019  00000000  00000000  0000100c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 66 .rodata.__FUNCTION__$5449 0000001a  00000000  00000000  00001028  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 67 .rodata.__FUNCTION__$5429 0000000d  00000000  00000000  00001044  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 68 .rodata.__FUNCTION__$5424 0000001e  00000000  00000000  00001054  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 69 .rodata.__FUNCTION__$5418 00000017  00000000  00000000  00001074  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 70 .rodata.__FUNCTION__$5412 00000017  00000000  00000000  0000108c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 71 .rodata.__FUNCTION__$5406 00000012  00000000  00000000  000010a4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 72 .rodata.__FUNCTION__$5397 00000015  00000000  00000000  000010b8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 73 .rodata.__FUNCTION__$5387 00000015  00000000  00000000  000010d0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 74 .rodata.__FUNCTION__$5376 00000012  00000000  00000000  000010e8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 75 .rodata.__FUNCTION__$5368 00000019  00000000  00000000  000010fc  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 76 .rodata.__FUNCTION__$5495 0000001b  00000000  00000000  00001118  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 77 .rodata.__FUNCTION__$5357 00000012  00000000  00000000  00001134  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 78 .rodata.__FUNCTION__$5346 0000001e  00000000  00000000  00001148  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 79 .rodata.__FUNCTION__$5340 00000019  00000000  00000000  00001168  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 80 .rodata.__FUNCTION__$5333 00000019  00000000  00000000  00001184  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 81 .rodata.__FUNCTION__$5306 00000014  00000000  00000000  000011a0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 82 .rodata.__FUNCTION__$5297 00000013  00000000  00000000  000011b4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 83 .debug_frame  00000310  00000000  00000000  000011c8  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 84 .debug_info   0000226e  00000000  00000000  000014d8  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 85 .debug_abbrev 00000247  00000000  00000000  00003746  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 86 .debug_loc    000010f0  00000000  00000000  0000398d  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 87 .debug_aranges 00000118  00000000  00000000  00004a7d  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 88 .debug_ranges 00000108  00000000  00000000  00004b95  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 89 .debug_line   00000e69  00000000  00000000  00004c9d  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 90 .debug_str    000009f1  00000000  00000000  00005b06  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 91 .comment      0000003b  00000000  00000000  000064f7  2**0
+                  CONTENTS, READONLY
+ 92 .xtensa.info  00000038  00000000  00000000  00006532  2**0
+                  CONTENTS, READONLY
+ 93 .xt.lit       000000e8  00000000  00000000  0000656a  2**0
+                  CONTENTS, RELOC, READONLY
+ 94 .xt.prop      00000e10  00000000  00000000  00006652  2**0
+                  CONTENTS, RELOC, READONLY
+
+ringbuf.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.returnItemToRingbufBytebuf 0000001c  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.returnItemToRingbufDefault 00000044  00000000  00000000  00000050  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.getItemFromRingbufDefault 00000024  00000000  00000000  00000094  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.copyItemToRingbufNoSplit 00000024  00000000  00000000  000000b8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.copyItemToRingbufByteBuf 00000008  00000000  00000000  000000dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.copyItemToRingbufAllowSplit 00000034  00000000  00000000  000000e4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .literal.xRingbufferReceiveGeneric 00000020  00000000  00000000  00000118  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.xRingbufferPrintInfo 00000020  00000000  00000000  00000138  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.xRingbufferGetCurFreeSize 0000001c  00000000  00000000  00000158  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .literal.xRingbufferCreate 00000064  00000000  00000000  00000174  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .literal.xRingbufferCreateNoSplit 00000004  00000000  00000000  000001d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .literal.vRingbufferDelete 00000010  00000000  00000000  000001dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 12 .literal.xRingbufferGetMaxItemSize 00000014  00000000  00000000  000001ec  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .literal.xRingbufferIsNextItemWrapped 00000014  00000000  00000000  00000200  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .literal.xRingbufferSend 0000003c  00000000  00000000  00000214  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 15 .literal.xRingbufferSendFromISR 00000024  00000000  00000000  00000250  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .literal.xRingbufferReceive 00000004  00000000  00000000  00000274  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .literal.xRingbufferReceiveFromISR 0000001c  00000000  00000000  00000278  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .literal.xRingbufferReceiveUpTo 00000020  00000000  00000000  00000294  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .literal.xRingbufferReceiveUpToFromISR 00000024  00000000  00000000  000002b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .literal.vRingbufferReturnItem 0000000c  00000000  00000000  000002d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .literal.vRingbufferReturnItemFromISR 0000000c  00000000  00000000  000002e4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 22 .literal.xRingbufferAddToQueueSetRead 00000018  00000000  00000000  000002f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .literal.xRingbufferAddToQueueSetWrite 00000018  00000000  00000000  00000308  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .literal.xRingbufferRemoveFromQueueSetRead 00000018  00000000  00000000  00000320  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 25 .literal.xRingbufferRemoveFromQueueSetWrite 00000018  00000000  00000000  00000338  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 26 .text         00000000  00000000  00000000  00000350  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 27 .data         00000000  00000000  00000000  00000350  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 28 .bss          00000000  00000000  00000000  00000350  2**0
+                  ALLOC
+ 29 .text.ringbufferFreeMem 00000015  00000000  00000000  00000350  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 30 .text.getItemFromRingbufByteBuf 00000056  00000000  00000000  00000368  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 31 .text.getCurFreeSizeByteBuf 00000015  00000000  00000000  000003c0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 32 .text.getCurFreeSizeAllowSplit 00000030  00000000  00000000  000003d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 33 .text.getCurFreeSizeNoSplit 00000024  00000000  00000000  00000408  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 34 .rodata.str1.4 00000083  00000000  00000000  0000042c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 35 .text.returnItemToRingbufBytebuf 00000045  00000000  00000000  000004b0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 36 .text.returnItemToRingbufDefault 00000136  00000000  00000000  000004f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 37 .text.getItemFromRingbufDefault 000000a5  00000000  00000000  00000630  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 38 .text.copyItemToRingbufNoSplit 000000b0  00000000  00000000  000006d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 39 .text.copyItemToRingbufByteBuf 00000046  00000000  00000000  00000788  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 40 .text.copyItemToRingbufAllowSplit 00000124  00000000  00000000  000007d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 41 .text.xRingbufferReceiveGeneric 00000070  00000000  00000000  000008f4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 42 .text.xRingbufferPrintInfo 00000046  00000000  00000000  00000964  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 43 .text.xRingbufferGetCurFreeSize 00000043  00000000  00000000  000009ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 44 .text.xRingbufferCreate 00000118  00000000  00000000  000009f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 45 .text.xRingbufferCreateNoSplit 0000001c  00000000  00000000  00000b08  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 46 .text.vRingbufferDelete 0000002c  00000000  00000000  00000b24  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 47 .text.xRingbufferGetMaxItemSize 00000022  00000000  00000000  00000b50  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 48 .text.xRingbufferIsNextItemWrapped 00000030  00000000  00000000  00000b74  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 49 .text.xRingbufferSend 000000d4  00000000  00000000  00000ba4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 50 .text.xRingbufferSendFromISR 00000062  00000000  00000000  00000c78  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 51 .text.xRingbufferReceive 00000015  00000000  00000000  00000cdc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 52 .text.xRingbufferReceiveFromISR 00000040  00000000  00000000  00000cf4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 53 .text.xRingbufferReceiveUpTo 00000054  00000000  00000000  00000d34  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 54 .text.xRingbufferReceiveUpToFromISR 00000064  00000000  00000000  00000d88  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 55 .text.vRingbufferReturnItem 00000030  00000000  00000000  00000dec  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 56 .text.vRingbufferReturnItemFromISR 0000002c  00000000  00000000  00000e1c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 57 .text.xRingbufferAddToQueueSetRead 0000002c  00000000  00000000  00000e48  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 58 .text.xRingbufferAddToQueueSetWrite 0000002c  00000000  00000000  00000e74  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 59 .text.xRingbufferRemoveFromQueueSetRead 0000002c  00000000  00000000  00000ea0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 60 .text.xRingbufferRemoveFromQueueSetWrite 0000002c  00000000  00000000  00000ecc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 61 .rodata.__FUNCTION__$5577 00000023  00000000  00000000  00000ef8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 62 .rodata.__FUNCTION__$5571 00000022  00000000  00000000  00000f1c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 63 .rodata.__FUNCTION__$5565 0000001e  00000000  00000000  00000f40  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 64 .rodata.__FUNCTION__$5559 0000001d  00000000  00000000  00000f60  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 65 .rodata.__FUNCTION__$5542 0000001e  00000000  00000000  00000f80  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 66 .rodata.__FUNCTION__$5534 00000017  00000000  00000000  00000fa0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 67 .rodata.__FUNCTION__$5527 0000001a  00000000  00000000  00000fb8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 68 .rodata.__FUNCTION__$5508 0000001a  00000000  00000000  00000fd4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 69 .rodata.__FUNCTION__$5497 00000017  00000000  00000000  00000ff0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 70 .rodata.__FUNCTION__$5482 00000010  00000000  00000000  00001008  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 71 .rodata.__FUNCTION__$5469 0000001d  00000000  00000000  00001018  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 72 .rodata.__FUNCTION__$5464 0000001a  00000000  00000000  00001038  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 73 .rodata.__FUNCTION__$5379 0000001c  00000000  00000000  00001054  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 74 .rodata.__FUNCTION__$5395 0000001a  00000000  00000000  00001070  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 75 .rodata.__FUNCTION__$5408 0000001b  00000000  00000000  0000108c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 76 .rodata.__FUNCTION__$5418 0000001b  00000000  00000000  000010a8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 77 .rodata.__FUNCTION__$5369 00000019  00000000  00000000  000010c4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 78 .rodata.__FUNCTION__$5450 00000012  00000000  00000000  000010e0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 79 .rodata.__FUNCTION__$5428 0000001a  00000000  00000000  000010f4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 80 .rodata.__FUNCTION__$5423 00000015  00000000  00000000  00001110  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 81 .debug_frame  000002f8  00000000  00000000  00001128  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 82 .debug_info   00001dc4  00000000  00000000  00001420  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 83 .debug_abbrev 000002c9  00000000  00000000  000031e4  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 84 .debug_loc    00000df4  00000000  00000000  000034ad  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 85 .debug_aranges 00000110  00000000  00000000  000042a1  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 86 .debug_ranges 00000130  00000000  00000000  000043b1  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 87 .debug_line   00000ce4  00000000  00000000  000044e1  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 88 .debug_str    000008b1  00000000  00000000  000051c5  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 89 .comment      0000003b  00000000  00000000  00005a76  2**0
+                  CONTENTS, READONLY
+ 90 .xtensa.info  00000038  00000000  00000000  00005ab1  2**0
+                  CONTENTS, READONLY
+ 91 .xt.lit       000000d0  00000000  00000000  00005ae9  2**0
+                  CONTENTS, RELOC, READONLY
+ 92 .xt.prop      00000bb8  00000000  00000000  00005bb9  2**0
+                  CONTENTS, RELOC, READONLY
+
+tasks.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.prvResetNextTaskUnblockTime 00000008  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.prvTaskGetSnapshotsFromList 00000004  00000000  00000000  0000003c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.prvDeleteTLS 00000018  00000000  00000000  00000040  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.prvTaskIsTaskSuspended 0000001c  00000000  00000000  00000058  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.prvInitialiseNewTask 00000018  00000000  00000000  00000074  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.prvInitialiseTaskLists 00000040  00000000  00000000  0000008c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .literal.prvDeleteTCB 0000002c  00000000  00000000  000000cc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.prvAddCurrentTaskToDelayedList 0000001c  00000000  00000000  000000f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.taskYIELD_OTHER_CORE 00000010  00000000  00000000  00000114  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .literal.vTaskEndScheduler 00000008  00000000  00000000  00000124  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .literal.vTaskSuspendAll 00000008  00000000  00000000  0000012c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .literal.uxTaskGetNumberOfTasks 00000004  00000000  00000000  00000134  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 12 .literal.xTaskGetIdleTaskHandle 0000001c  00000000  00000000  00000138  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .literal.xTaskGetIdleTaskHandleForCPU 0000001c  00000000  00000000  00000154  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .literal.vTaskSwitchContext 0000003c  00000000  00000000  00000170  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 15 .literal.vTaskSetTimeOutState 00000020  00000000  00000000  000001ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .literal.vTaskMissedYield 00000004  00000000  00000000  000001cc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .literal.vTaskAllocateMPURegions 00000020  00000000  00000000  000001d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .literal.xTaskGetCurrentTaskHandle 00000008  00000000  00000000  000001f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .literal.__getreent 00000008  00000000  00000000  000001f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .literal.pcTaskGetTaskName 0000001c  00000000  00000000  00000200  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .literal.pvTaskGetThreadLocalStoragePointer 00000004  00000000  00000000  0000021c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 22 .literal.xTaskGetAffinity 00000004  00000000  00000000  00000220  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .literal.uxTaskGetStackHighWaterMark 00000008  00000000  00000000  00000224  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .literal.pxTaskGetStackStart 00000004  00000000  00000000  0000022c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 25 .literal.xTaskGetCurrentTaskHandleForCPU 00000004  00000000  00000000  00000230  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 26 .literal.xTaskGetSchedulerState 0000000c  00000000  00000000  00000234  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 27 .literal.vTaskEnterCritical 00000034  00000000  00000000  00000240  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 28 .literal.vTaskExitCritical 00000030  00000000  00000000  00000274  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 29 .literal.prvAddNewTaskToReadyList 00000058  00000000  00000000  000002a4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 30 .literal.xTaskCreateRestricted 00000028  00000000  00000000  000002fc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 31 .literal.xTaskCreatePinnedToCore 00000018  00000000  00000000  00000324  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 32 .literal.vTaskStartScheduler 00000038  00000000  00000000  0000033c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 33 .literal.vTaskDelete 00000060  00000000  00000000  00000374  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 34 .literal.vTaskDelayUntil 00000048  00000000  00000000  000003d4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 35 .literal.vTaskDelay 00000038  00000000  00000000  0000041c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 36 .literal.eTaskGetState 00000038  00000000  00000000  00000454  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 37 .literal.uxTaskPriorityGet 00000010  00000000  00000000  0000048c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 38 .literal.uxTaskPriorityGetFromISR 00000010  00000000  00000000  0000049c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 39 .literal.vTaskPrioritySet 00000044  00000000  00000000  000004ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 40 .literal.vTaskSuspend 00000060  00000000  00000000  000004f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 41 .literal.vTaskResume 00000044  00000000  00000000  00000550  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 42 .literal.xTaskResumeFromISR 0000004c  00000000  00000000  00000594  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 43 .literal.prvCheckTasksWaitingTermination 00000030  00000000  00000000  000005e0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 44 .literal.prvIdleTask 00000008  00000000  00000000  00000610  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 45 .literal.xTaskGetTickCount 00000010  00000000  00000000  00000618  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 46 .literal.xTaskGetTickCountFromISR 00000010  00000000  00000000  00000628  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 47 .literal.xTaskIncrementTick 00000078  00000000  00000000  00000638  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 48 .literal.xTaskResumeAll 0000005c  00000000  00000000  000006b0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 49 .literal.vTaskPlaceOnEventList 00000040  00000000  00000000  0000070c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 50 .literal.vTaskPlaceOnUnorderedEventList 00000054  00000000  00000000  0000074c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 51 .literal.vTaskPlaceOnEventListRestricted 00000038  00000000  00000000  000007a0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 52 .literal.xTaskRemoveFromEventList 00000058  00000000  00000000  000007d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 53 .literal.xTaskRemoveFromUnorderedEventList 0000005c  00000000  00000000  00000830  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 54 .literal.xTaskCheckForTimeOut 0000003c  00000000  00000000  0000088c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 55 .literal.vTaskSetThreadLocalStoragePointerAndDelCallback 00000010  00000000  00000000  000008c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 56 .literal.vTaskSetThreadLocalStoragePointer 00000004  00000000  00000000  000008d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 57 .literal.vTaskPriorityInherit 0000002c  00000000  00000000  000008dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 58 .literal.xTaskPriorityDisinherit 00000040  00000000  00000000  00000908  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 59 .literal.uxTaskResetEventItemValue 00000010  00000000  00000000  00000948  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 60 .literal.pvTaskIncrementMutexHeldCount 00000010  00000000  00000000  00000958  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 61 .literal.ulTaskNotifyTake 00000030  00000000  00000000  00000968  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 62 .literal.xTaskNotifyWait 00000030  00000000  00000000  00000998  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 63 .literal.xTaskNotify 00000050  00000000  00000000  000009c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 64 .literal.xTaskNotifyFromISR 00000058  00000000  00000000  00000a18  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 65 .literal.vTaskNotifyGiveFromISR 00000058  00000000  00000000  00000a70  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 66 .literal.uxTaskGetSnapshotAll 00000030  00000000  00000000  00000ac8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 67 .text         00000000  00000000  00000000  00000af8  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 68 .data         00000000  00000000  00000000  00000af8  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 69 .bss          00000000  00000000  00000000  00000af8  2**0
+                  ALLOC
+ 70 .text.prvTaskCheckFreeStackSpace 00000019  00000000  00000000  00000af8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 71 .text.prvResetNextTaskUnblockTime 00000034  00000000  00000000  00000b14  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 72 .text.prvTaskGetSnapshot 00000036  00000000  00000000  00000b48  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 73 .text.prvTaskGetSnapshotsFromList 00000047  00000000  00000000  00000b80  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 74 .rodata.str1.4 00000161  00000000  00000000  00000bc8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 75 .text.prvDeleteTLS 00000040  00000000  00000000  00000d2c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 76 .text.prvTaskIsTaskSuspended 0000004e  00000000  00000000  00000d6c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 77 .text.prvInitialiseNewTask 000000d0  00000000  00000000  00000dbc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 78 .text.prvInitialiseTaskLists 0000006e  00000000  00000000  00000e8c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 79 .text.prvDeleteTCB 0000005d  00000000  00000000  00000efc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 80 .text.prvAddCurrentTaskToDelayedList 0000006a  00000000  00000000  00000f5c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 81 .text.taskYIELD_OTHER_CORE 00000053  00000000  00000000  00000fc8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 82 .text.vTaskEndScheduler 00000018  00000000  00000000  0000101c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 83 .text.vTaskSuspendAll 00000026  00000000  00000000  00001034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 84 .text.uxTaskGetNumberOfTasks 0000000d  00000000  00000000  0000105c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 85 .text.xTaskGetIdleTaskHandle 0000003b  00000000  00000000  0000106c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 86 .text.xTaskGetIdleTaskHandleForCPU 0000002e  00000000  00000000  000010a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 87 .text.vTaskSwitchContext 0000028a  00000000  00000000  000010d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 88 .text.vTaskSetTimeOutState 00000034  00000000  00000000  00001364  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 89 .text.vTaskMissedYield 00000018  00000000  00000000  00001398  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 90 .text.vTaskAllocateMPURegions 00000028  00000000  00000000  000013b0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 91 .text.xTaskGetCurrentTaskHandle 0000001f  00000000  00000000  000013d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 92 .text.__getreent 00000019  00000000  00000000  000013f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 93 .text.pcTaskGetTaskName 0000002d  00000000  00000000  00001414  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 94 .text.pvTaskGetThreadLocalStoragePointer 0000001e  00000000  00000000  00001444  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 95 .text.xTaskGetAffinity 00000013  00000000  00000000  00001464  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 96 .text.uxTaskGetStackHighWaterMark 0000001a  00000000  00000000  00001478  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 97 .text.pxTaskGetStackStart 00000012  00000000  00000000  00001494  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 98 .text.xTaskGetCurrentTaskHandleForCPU 00000018  00000000  00000000  000014a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 99 .text.xTaskGetSchedulerState 00000037  00000000  00000000  000014c0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+100 .text.vTaskEnterCritical 000000b3  00000000  00000000  000014f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+101 .text.vTaskExitCritical 0000008a  00000000  00000000  000015ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+102 .text.prvAddNewTaskToReadyList 00000173  00000000  00000000  00001638  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+103 .text.xTaskCreateRestricted 00000074  00000000  00000000  000017ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+104 .text.xTaskCreatePinnedToCore 00000076  00000000  00000000  00001820  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+105 .text.vTaskStartScheduler 00000072  00000000  00000000  00001898  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+106 .text.vTaskDelete 00000122  00000000  00000000  0000190c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+107 .text.vTaskDelayUntil 000000e7  00000000  00000000  00001a30  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+108 .text.vTaskDelay 00000080  00000000  00000000  00001b18  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+109 .text.eTaskGetState 000000aa  00000000  00000000  00001b98  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+110 .text.uxTaskPriorityGet 00000023  00000000  00000000  00001c44  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+111 .text.uxTaskPriorityGetFromISR 00000023  00000000  00000000  00001c68  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+112 .text.vTaskPrioritySet 00000143  00000000  00000000  00001c8c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+113 .text.vTaskSuspend 00000106  00000000  00000000  00001dd0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+114 .text.vTaskResume 000000d8  00000000  00000000  00001ed8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+115 .text.xTaskResumeFromISR 000000f6  00000000  00000000  00001fb0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+116 .text.prvCheckTasksWaitingTermination 000000c6  00000000  00000000  000020a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+117 .text.prvIdleTask 00000012  00000000  00000000  00002170  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+118 .text.xTaskGetTickCount 00000020  00000000  00000000  00002184  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+119 .text.xTaskGetTickCountFromISR 00000020  00000000  00000000  000021a4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+120 .text.xTaskIncrementTick 000001ce  00000000  00000000  000021c4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+121 .text.xTaskResumeAll 00000186  00000000  00000000  00002394  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+122 .text.vTaskPlaceOnEventList 00000097  00000000  00000000  0000251c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+123 .text.vTaskPlaceOnUnorderedEventList 000000cf  00000000  00000000  000025b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+124 .text.vTaskPlaceOnEventListRestricted 0000006f  00000000  00000000  00002684  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+125 .text.xTaskRemoveFromEventList 00000143  00000000  00000000  000026f4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+126 .text.xTaskRemoveFromUnorderedEventList 000000ff  00000000  00000000  00002838  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+127 .text.xTaskCheckForTimeOut 00000096  00000000  00000000  00002938  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+128 .text.vTaskSetThreadLocalStoragePointerAndDelCallback 0000002f  00000000  00000000  000029d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+129 .text.vTaskSetThreadLocalStoragePointer 00000013  00000000  00000000  00002a00  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+130 .text.vTaskPriorityInherit 000000db  00000000  00000000  00002a14  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+131 .text.xTaskPriorityDisinherit 000000aa  00000000  00000000  00002af0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+132 .text.uxTaskResetEventItemValue 0000003e  00000000  00000000  00002b9c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+133 .text.pvTaskIncrementMutexHeldCount 00000054  00000000  00000000  00002bdc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+134 .text.ulTaskNotifyTake 0000012e  00000000  00000000  00002c30  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+135 .text.xTaskNotifyWait 00000156  00000000  00000000  00002d60  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+136 .text.xTaskNotify 0000014a  00000000  00000000  00002eb8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+137 .text.xTaskNotifyFromISR 00000172  00000000  00000000  00003004  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+138 .text.vTaskNotifyGiveFromISR 0000011c  00000000  00000000  00003178  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+139 .text.uxTaskGetSnapshotAll 0000009c  00000000  00000000  00003294  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+140 .rodata.__FUNCTION__$5930 00000017  00000000  00000000  00003330  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+141 .rodata.__FUNCTION__$5917 00000013  00000000  00000000  00003348  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+142 .rodata.__FUNCTION__$5901 0000000c  00000000  00000000  0000335c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+143 .rodata.__func__$5851 00000029  00000000  00000000  00003368  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+144 .rodata.__func__$5846 00000029  00000000  00000000  00003394  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+145 .rodata.__FUNCTION__$5808 00000018  00000000  00000000  000033c0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+146 .rodata.__FUNCTION__$5718 00000018  00000000  00000000  000033d8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+147 .rodata.__FUNCTION__$5683 00000015  00000000  00000000  000033f0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+148 .rodata.__FUNCTION__$5677 00000015  00000000  00000000  00003408  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+149 .rodata.__FUNCTION__$5673 00000022  00000000  00000000  00003420  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+150 .rodata.__FUNCTION__$5663 00000019  00000000  00000000  00003444  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+151 .rodata.__FUNCTION__$5654 00000020  00000000  00000000  00003460  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+152 .rodata.__FUNCTION__$5648 0000001f  00000000  00000000  00003480  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+153 .rodata.__FUNCTION__$5641 00000016  00000000  00000000  000034a0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+154 .rodata.ucExpectedStackBytes$5613 00000014  00000000  00000000  000034b8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+155 .rodata.__FUNCTION__$5605 00000013  00000000  00000000  000034cc  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+156 .rodata.__FUNCTION__$5596 0000001d  00000000  00000000  000034e0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+157 .rodata.__FUNCTION__$5591 00000017  00000000  00000000  00003500  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+158 .rodata.__FUNCTION__$5587 00000012  00000000  00000000  00003518  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+159 .rodata.__FUNCTION__$5565 0000000f  00000000  00000000  0000352c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+160 .rodata.__FUNCTION__$5547 00000014  00000000  00000000  0000353c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+161 .rodata.__FUNCTION__$5536 00000013  00000000  00000000  00003550  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+162 .rodata.__FUNCTION__$5525 00000017  00000000  00000000  00003564  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+163 .rodata.__FUNCTION__$5530 0000000c  00000000  00000000  0000357c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+164 .rodata.__FUNCTION__$5519 0000000d  00000000  00000000  00003588  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+165 .rodata.__FUNCTION__$5513 00000011  00000000  00000000  00003598  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+166 .rodata.__FUNCTION__$5494 0000000e  00000000  00000000  000035ac  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+167 .rodata.__FUNCTION__$5485 0000000b  00000000  00000000  000035bc  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+168 .rodata.__FUNCTION__$5478 00000010  00000000  00000000  000035c8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+169 .rodata.__FUNCTION__$5772 0000000d  00000000  00000000  000035d8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+170 .rodata.__FUNCTION__$5776 0000000d  00000000  00000000  000035e8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+171 .rodata.__FUNCTION__$5470 0000000c  00000000  00000000  000035f8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+172 .rodata.__FUNCTION__$5463 00000019  00000000  00000000  00003604  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+173 .rodata.__FUNCTION__$5421 00000016  00000000  00000000  00003620  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+174 .bss.xSwitchingContext 00000008  00000000  00000000  00003638  2**2
+                  ALLOC
+175 .data.xTickCountMutex 00000008  00000000  00000000  00003638  2**2
+                  CONTENTS, ALLOC, LOAD, DATA
+176 .data.xTaskQueueMutex 00000008  00000000  00000000  00003640  2**2
+                  CONTENTS, ALLOC, LOAD, DATA
+177 .bss.uxSchedulerSuspended 00000008  00000000  00000000  00003648  2**2
+                  ALLOC
+178 .data.xNextTaskUnblockTime 00000004  00000000  00000000  00003648  2**2
+                  CONTENTS, ALLOC, LOAD, DATA
+179 .bss.uxTaskNumber 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+180 .bss.xNumOfOverflows 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+181 .bss.xYieldPending 00000008  00000000  00000000  0000364c  2**2
+                  ALLOC
+182 .bss.uxPendedTicks 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+183 .bss.xSchedulerRunning 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+184 .bss.uxTopReadyPriority 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+185 .bss.xTickCount 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+186 .bss.uxCurrentNumberOfTasks 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+187 .bss.xIdleTaskHandle 00000008  00000000  00000000  0000364c  2**2
+                  ALLOC
+188 .bss.xSuspendedTaskList 00000014  00000000  00000000  0000364c  2**2
+                  ALLOC
+189 .bss.uxTasksDeleted 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+190 .bss.xTasksWaitingTermination 00000014  00000000  00000000  0000364c  2**2
+                  ALLOC
+191 .bss.xPendingReadyList 00000028  00000000  00000000  0000364c  2**2
+                  ALLOC
+192 .bss.pxOverflowDelayedTaskList 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+193 .bss.pxDelayedTaskList 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+194 .bss.xDelayedTaskList2 00000014  00000000  00000000  0000364c  2**2
+                  ALLOC
+195 .bss.xDelayedTaskList1 00000014  00000000  00000000  0000364c  2**2
+                  ALLOC
+196 .bss.pxReadyTasksLists 000001f4  00000000  00000000  0000364c  2**2
+                  ALLOC
+197 .bss.pxCurrentTCB 00000008  00000000  00000000  0000364c  2**2
+                  ALLOC
+198 .debug_frame  00000688  00000000  00000000  0000364c  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+199 .debug_info   00005e20  00000000  00000000  00003cd4  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+200 .debug_abbrev 0000046f  00000000  00000000  00009af4  2**0
+                  CONTENTS, READONLY, DEBUGGING
+201 .debug_loc    000022ef  00000000  00000000  00009f63  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+202 .debug_aranges 00000240  00000000  00000000  0000c252  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+203 .debug_ranges 000002c8  00000000  00000000  0000c492  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+204 .debug_line   0000291c  00000000  00000000  0000c75a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+205 .debug_str    0000190d  00000000  00000000  0000f076  2**0
+                  CONTENTS, READONLY, DEBUGGING
+206 .comment      0000003b  00000000  00000000  00010983  2**0
+                  CONTENTS, READONLY
+207 .xtensa.info  00000038  00000000  00000000  000109be  2**0
+                  CONTENTS, READONLY
+208 .xt.lit       00000218  00000000  00000000  000109f6  2**0
+                  CONTENTS, RELOC, READONLY
+209 .xt.prop      00002154  00000000  00000000  00010c0e  2**0
+                  CONTENTS, RELOC, READONLY
+
+timers.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.prvGetNextExpireTime 00000004  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.prvInsertTimerInActiveList 00000010  00000000  00000000  00000038  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.prvCheckForValidListAndQueue 00000044  00000000  00000000  00000048  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.prvInitialiseNewTimer 0000001c  00000000  00000000  0000008c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.xTimerCreateTimerTask 0000002c  00000000  00000000  000000a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.xTimerCreate 00000008  00000000  00000000  000000d4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .literal.xTimerGenericCommand 00000014  00000000  00000000  000000dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.prvSwitchTimerLists 00000028  00000000  00000000  000000f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.prvSampleTimeNow 0000000c  00000000  00000000  00000118  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .literal.prvProcessExpiredTimer 00000024  00000000  00000000  00000124  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .literal.prvProcessTimerOrBlockTask 00000024  00000000  00000000  00000148  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .literal.prvProcessReceivedCommands 00000040  00000000  00000000  0000016c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 12 .literal.prvTimerTask 0000000c  00000000  00000000  000001ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .literal.xTimerGetPeriod 00000014  00000000  00000000  000001b8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .literal.xTimerGetExpiryTime 00000014  00000000  00000000  000001cc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 15 .literal.xTimerIsTimerActive 0000000c  00000000  00000000  000001e0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .literal.vTimerSetTimerID 00000014  00000000  00000000  000001ec  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .literal.xTimerPendFunctionCallFromISR 00000008  00000000  00000000  00000200  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .literal.xTimerPendFunctionCall 0000001c  00000000  00000000  00000208  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .text         00000000  00000000  00000000  00000224  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 20 .data         00000000  00000000  00000000  00000224  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 21 .bss          00000000  00000000  00000000  00000224  2**0
+                  ALLOC
+ 22 .text.prvGetNextExpireTime 00000020  00000000  00000000  00000224  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .text.prvInsertTimerInActiveList 00000054  00000000  00000000  00000244  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .rodata.str1.4 00000058  00000000  00000000  00000298  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 25 .text.prvCheckForValidListAndQueue 0000007e  00000000  00000000  000002f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 26 .text.prvInitialiseNewTimer 0000003a  00000000  00000000  00000370  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 27 .text.xTimerCreateTimerTask 0000004c  00000000  00000000  000003ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 28 .text.xTimerCreate 00000026  00000000  00000000  000003f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 29 .text.xTimerGenericCommand 0000005d  00000000  00000000  00000420  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 30 .text.prvSwitchTimerLists 00000082  00000000  00000000  00000480  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 31 .text.prvSampleTimeNow 0000002f  00000000  00000000  00000504  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 32 .text.prvProcessExpiredTimer 0000005f  00000000  00000000  00000534  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 33 .text.prvProcessTimerOrBlockTask 0000006c  00000000  00000000  00000594  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 34 .text.prvProcessReceivedCommands 000000e6  00000000  00000000  00000600  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 35 .rodata.prvProcessReceivedCommands 00000028  00000000  00000000  000006e8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
+ 36 .text.prvTimerTask 0000001d  00000000  00000000  00000710  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 37 .text.xTimerGetPeriod 00000022  00000000  00000000  00000730  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 38 .text.xTimerGetExpiryTime 00000022  00000000  00000000  00000754  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 39 .text.pcTimerGetTimerName 00000007  00000000  00000000  00000778  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 40 .text.xTimerIsTimerActive 00000024  00000000  00000000  00000780  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 41 .text.pvTimerGetTimerID 00000007  00000000  00000000  000007a4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 42 .text.vTimerSetTimerID 00000022  00000000  00000000  000007ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 43 .text.xTimerPendFunctionCallFromISR 00000022  00000000  00000000  000007d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 44 .text.xTimerPendFunctionCall 0000003c  00000000  00000000  000007f4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 45 .rodata.__FUNCTION__$5352 00000017  00000000  00000000  00000830  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 46 .rodata.__FUNCTION__$5335 00000011  00000000  00000000  00000848  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 47 .rodata.__FUNCTION__$5240 00000014  00000000  00000000  0000085c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 48 .rodata.__FUNCTION__$5234 00000010  00000000  00000000  00000870  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 49 .rodata.__FUNCTION__$5220 00000016  00000000  00000000  00000880  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 50 .rodata.__FUNCTION__$5320 0000001d  00000000  00000000  00000898  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 51 .rodata.__FUNCTION__$5289 0000001b  00000000  00000000  000008b8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 52 .rodata.__FUNCTION__$5251 00000017  00000000  00000000  000008d4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 53 .rodata.__FUNCTION__$5313 00000014  00000000  00000000  000008ec  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 54 .bss.xLastTime$5272 00000004  00000000  00000000  00000900  2**2
+                  ALLOC
+ 55 .rodata.__FUNCTION__$5203 00000016  00000000  00000000  00000900  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 56 .data.xTimerMux 00000008  00000000  00000000  00000918  2**2
+                  CONTENTS, ALLOC, LOAD, DATA
+ 57 .bss.xTimerQueue 00000004  00000000  00000000  00000920  2**2
+                  ALLOC
+ 58 .bss.pxOverflowTimerList 00000004  00000000  00000000  00000920  2**2
+                  ALLOC
+ 59 .bss.pxCurrentTimerList 00000004  00000000  00000000  00000920  2**2
+                  ALLOC
+ 60 .bss.xActiveTimerList2 00000014  00000000  00000000  00000920  2**2
+                  ALLOC
+ 61 .bss.xActiveTimerList1 00000014  00000000  00000000  00000920  2**2
+                  ALLOC
+ 62 .debug_frame  00000208  00000000  00000000  00000920  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 63 .debug_info   00001405  00000000  00000000  00000b28  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 64 .debug_abbrev 00000279  00000000  00000000  00001f2d  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 65 .debug_loc    0000062f  00000000  00000000  000021a6  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 66 .debug_aranges 000000c0  00000000  00000000  000027d5  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 67 .debug_ranges 000000b0  00000000  00000000  00002895  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 68 .debug_line   00000771  00000000  00000000  00002945  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 69 .debug_str    00000934  00000000  00000000  000030b6  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 70 .comment      0000003b  00000000  00000000  000039ea  2**0
+                  CONTENTS, READONLY
+ 71 .xtensa.info  00000038  00000000  00000000  00003a25  2**0
+                  CONTENTS, READONLY
+ 72 .xt.lit       00000098  00000000  00000000  00003a5d  2**0
+                  CONTENTS, RELOC, READONLY
+ 73 .xt.prop      00000714  00000000  00000000  00003af5  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_context.S.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal      00000024  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .text         0000013a  00000000  00000000  00000058  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .data         00000000  00000000  00000000  00000192  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  3 .bss          00000000  00000000  00000000  00000192  2**0
+                  ALLOC
+  4 .xtensa.info  00000038  00000000  00000000  00000192  2**0
+                  CONTENTS, READONLY
+  5 .debug_line   00000313  00000000  00000000  000001ca  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  6 .debug_info   0000009a  00000000  00000000  000004dd  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  7 .debug_abbrev 00000014  00000000  00000000  00000577  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  8 .debug_aranges 00000020  00000000  00000000  00000590  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  9 .xt.lit       00000008  00000000  00000000  000005b0  2**0
+                  CONTENTS, RELOC, READONLY
+ 10 .xt.prop      000000e4  00000000  00000000  000005b8  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_init.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal._xt_tick_divisor_init 0000000c  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.xt_clock_freq 00000004  00000000  00000000  00000040  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .text         00000000  00000000  00000000  00000044  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  3 .data         00000000  00000000  00000000  00000044  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  4 .bss          00000000  00000000  00000000  00000044  2**0
+                  ALLOC
+  5 .text._xt_tick_divisor_init 0000001f  00000000  00000000  00000044  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .text.xt_clock_freq 0000000d  00000000  00000000  00000064  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .bss._xt_tick_divisor 00000004  00000000  00000000  00000074  2**2
+                  ALLOC
+  8 .debug_frame  00000040  00000000  00000000  00000074  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  9 .debug_info   000000d4  00000000  00000000  000000b4  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 10 .debug_abbrev 0000008b  00000000  00000000  00000188  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 11 .debug_aranges 00000028  00000000  00000000  00000213  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 12 .debug_ranges 00000018  00000000  00000000  0000023b  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 13 .debug_line   000000c8  00000000  00000000  00000253  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 14 .debug_str    00000180  00000000  00000000  0000031b  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 15 .comment      0000003b  00000000  00000000  0000049b  2**0
+                  CONTENTS, READONLY
+ 16 .xtensa.info  00000038  00000000  00000000  000004d6  2**0
+                  CONTENTS, READONLY
+ 17 .xt.lit       00000010  00000000  00000000  0000050e  2**0
+                  CONTENTS, RELOC, READONLY
+ 18 .xt.prop      0000006c  00000000  00000000  0000051e  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_intr.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.xt_unhandled_interrupt 00000008  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.xt_set_exception_handler 00000008  00000000  00000000  0000003c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.xt_set_interrupt_handler 0000000c  00000000  00000000  00000044  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .text         00000000  00000000  00000000  00000050  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  4 .data         00000000  00000000  00000000  00000050  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  5 .bss          00000000  00000000  00000000  00000050  2**0
+                  ALLOC
+  6 .rodata.str1.4 00000023  00000000  00000000  00000050  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+  7 .text.xt_unhandled_interrupt 00000016  00000000  00000000  00000074  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .text.xt_set_exception_handler 00000042  00000000  00000000  0000008c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .text.xt_set_interrupt_handler 00000046  00000000  00000000  000000d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .debug_frame  00000058  00000000  00000000  00000118  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 11 .debug_info   0000042d  00000000  00000000  00000170  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 12 .debug_abbrev 000001b6  00000000  00000000  0000059d  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 13 .debug_loc    0000015c  00000000  00000000  00000753  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 14 .debug_aranges 00000030  00000000  00000000  000008af  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 15 .debug_ranges 00000020  00000000  00000000  000008df  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 16 .debug_line   000002b8  00000000  00000000  000008ff  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 17 .debug_str    000002a5  00000000  00000000  00000bb7  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 18 .comment      0000003b  00000000  00000000  00000e5c  2**0
+                  CONTENTS, READONLY
+ 19 .xtensa.info  00000038  00000000  00000000  00000e97  2**0
+                  CONTENTS, READONLY
+ 20 .xt.lit       00000018  00000000  00000000  00000ecf  2**0
+                  CONTENTS, RELOC, READONLY
+ 21 .xt.prop      00000120  00000000  00000000  00000ee7  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_intr_asm.S.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .text         00000033  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  1 .data         00000400  00000000  00000000  00000068  2**3
+                  CONTENTS, ALLOC, LOAD, RELOC, DATA
+  2 .bss          00000000  00000000  00000000  00000468  2**0
+                  ALLOC
+  3 .xtensa.info  00000038  00000000  00000000  00000468  2**0
+                  CONTENTS, READONLY
+  4 .debug_line   000000da  00000000  00000000  000004a0  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  5 .debug_info   0000009b  00000000  00000000  0000057a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  6 .debug_abbrev 00000014  00000000  00000000  00000615  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  7 .debug_aranges 00000020  00000000  00000000  00000630  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  8 .xt.prop      00000054  00000000  00000000  00000650  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_overlay_os_hook.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.xt_overlay_init_os 00000008  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.xt_overlay_lock 00000008  00000000  00000000  0000003c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.xt_overlay_unlock 00000008  00000000  00000000  00000044  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .text         00000000  00000000  00000000  0000004c  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  4 .data         00000000  00000000  00000000  0000004c  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  5 .bss          00000000  00000000  00000000  0000004c  2**0
+                  ALLOC
+  6 .text.xt_overlay_init_os 00000013  00000000  00000000  0000004c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .text.xt_overlay_lock 00000016  00000000  00000000  00000060  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .text.xt_overlay_unlock 00000016  00000000  00000000  00000078  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .bss.xt_overlay_mutex 00000004  00000000  00000000  00000090  2**2
+                  ALLOC
+ 10 .debug_frame  00000058  00000000  00000000  00000090  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 11 .debug_info   00000193  00000000  00000000  000000e8  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 12 .debug_abbrev 0000008f  00000000  00000000  0000027b  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 13 .debug_aranges 00000030  00000000  00000000  0000030a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 14 .debug_ranges 00000020  00000000  00000000  0000033a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 15 .debug_line   00000192  00000000  00000000  0000035a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 16 .debug_str    0000025c  00000000  00000000  000004ec  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 17 .comment      0000003b  00000000  00000000  00000748  2**0
+                  CONTENTS, READONLY
+ 18 .xtensa.info  00000038  00000000  00000000  00000783  2**0
+                  CONTENTS, READONLY
+ 19 .xt.lit       00000018  00000000  00000000  000007bb  2**0
+                  CONTENTS, RELOC, READONLY
+ 20 .xt.prop      0000009c  00000000  00000000  000007d3  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_vector_defaults.S.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .iram1.literal 00000004  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .text         00000000  00000000  00000000  00000038  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  2 .data         00000000  00000000  00000000  00000038  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  3 .bss          00000000  00000000  00000000  00000038  2**0
+                  ALLOC
+  4 .iram1        00000032  00000000  00000000  00000038  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .xtensa.info  00000038  00000000  00000000  0000006a  2**0
+                  CONTENTS, READONLY
+  6 .debug_line   000000c2  00000000  00000000  000000a2  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  7 .debug_info   000000a2  00000000  00000000  00000164  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  8 .debug_abbrev 00000014  00000000  00000000  00000206  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  9 .debug_aranges 00000020  00000000  00000000  00000220  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 10 .xt.lit       00000008  00000000  00000000  00000240  2**0
+                  CONTENTS, RELOC, READONLY
+ 11 .xt.prop      00000078  00000000  00000000  00000248  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_vectors.S.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .iram1.literal 000000a8  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .DebugExceptionVector.literal 00000004  00000000  00000000  000000dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .DoubleExceptionVector.literal 00000004  00000000  00000000  000000e0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .KernelExceptionVector.literal 00000004  00000000  00000000  000000e4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .UserExceptionVector.literal 00000004  00000000  00000000  000000e8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .Level2InterruptVector.literal 00000004  00000000  00000000  000000ec  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .Level3InterruptVector.literal 00000004  00000000  00000000  000000f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .Level4InterruptVector.literal 00000004  00000000  00000000  000000f4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .Level5InterruptVector.literal 00000004  00000000  00000000  000000f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .NMIExceptionVector.literal 00000004  00000000  00000000  000000fc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .text         00000000  00000000  00000000  00000100  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 11 .data         00000008  00000000  00000000  00000100  2**4
+                  CONTENTS, ALLOC, LOAD, DATA
+ 12 .bss          00000000  00000000  00000000  00000108  2**0
+                  ALLOC
+ 13 .iram1        000004ec  00000000  00000000  00000108  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .rodata       00000024  00000000  00000000  00000600  2**4
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 15 .DebugExceptionVector.text 00000009  00000000  00000000  00000624  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .DoubleExceptionVector.text 00000011  00000000  00000000  00000630  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .KernelExceptionVector.text 00000009  00000000  00000000  00000644  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .UserExceptionVector.text 00000009  00000000  00000000  00000650  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .Level2InterruptVector.text 00000009  00000000  00000000  0000065c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .Level3InterruptVector.text 00000009  00000000  00000000  00000668  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .Level4InterruptVector.text 00000009  00000000  00000000  00000674  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 22 .Level5InterruptVector.text 00000009  00000000  00000000  00000680  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .NMIExceptionVector.text 00000009  00000000  00000000  0000068c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .WindowVectors.text 0000016a  00000000  00000000  00000698  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 25 .UserEnter.text 00000000  00000000  00000000  00000804  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 26 .xtensa.info  00000038  00000000  00000000  00000804  2**0
+                  CONTENTS, READONLY
+ 27 .debug_line   000009b8  00000000  00000000  0000083c  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 28 .debug_info   00000096  00000000  00000000  000011f4  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 29 .debug_abbrev 00000012  00000000  00000000  0000128a  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 30 .debug_aranges 00000070  00000000  00000000  000012a0  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 31 .debug_ranges 00000068  00000000  00000000  00001310  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 32 .xt.lit       00000050  00000000  00000000  00001378  2**0
+                  CONTENTS, RELOC, READONLY
+ 33 .xt.prop      000004f8  00000000  00000000  000013c8  2**0
+                  CONTENTS, RELOC, READONLY
diff --git a/tools/ldgen/samples/template.ld b/tools/ldgen/samples/template.ld
new file mode 100644 (file)
index 0000000..02a0d21
--- /dev/null
@@ -0,0 +1,217 @@
+/*  Default entry point:  */
+ENTRY(call_start_cpu0);
+
+SECTIONS
+{
+  /* RTC fast memory holds RTC wake stub code,
+     including from any source file named rtc_wake_stub*.c
+  */
+  .rtc.text :
+  {
+    . = ALIGN(4);
+
+    mapping[rtc_text]
+
+    *rtc_wake_stub*.o(.literal .text .literal.* .text.*)
+  } >rtc_iram_seg
+
+  /* RTC slow memory holds RTC wake stub
+     data/rodata, including from any source file
+     named rtc_wake_stub*.c
+  */
+  .rtc.data :
+  {
+    _rtc_data_start = ABSOLUTE(.);
+
+    mapping[rtc_data]
+
+    *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*)
+    _rtc_data_end = ABSOLUTE(.);
+  } > rtc_slow_seg
+
+  /* RTC bss, from any source file named rtc_wake_stub*.c */
+  .rtc.bss (NOLOAD) :
+  {
+    _rtc_bss_start = ABSOLUTE(.);
+
+    mapping[rtc_bss]
+
+    *rtc_wake_stub*.o(.bss .bss.*)
+    *rtc_wake_stub*.o(COMMON)
+    _rtc_bss_end = ABSOLUTE(.);
+  } > rtc_slow_seg
+
+  /* Send .iram0 code to iram */
+  .iram0.vectors :
+  {
+    /* Vectors go to IRAM */
+    _init_start = ABSOLUTE(.);
+    /* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */
+    . = 0x0;
+    KEEP(*(.WindowVectors.text));
+    . = 0x180;
+    KEEP(*(.Level2InterruptVector.text));
+    . = 0x1c0;
+    KEEP(*(.Level3InterruptVector.text));
+    . = 0x200;
+    KEEP(*(.Level4InterruptVector.text));
+    . = 0x240;
+    KEEP(*(.Level5InterruptVector.text));
+    . = 0x280;
+    KEEP(*(.DebugExceptionVector.text));
+    . = 0x2c0;
+    KEEP(*(.NMIExceptionVector.text));
+    . = 0x300;
+    KEEP(*(.KernelExceptionVector.text));
+    . = 0x340;
+    KEEP(*(.UserExceptionVector.text));
+    . = 0x3C0;
+    KEEP(*(.DoubleExceptionVector.text));
+    . = 0x400;
+    *(.*Vector.literal)
+
+    *(.UserEnter.literal);
+    *(.UserEnter.text);
+    . = ALIGN (16);
+    *(.entry.text)
+    *(.init.literal)
+    *(.init)
+    _init_end = ABSOLUTE(.);
+
+    /* This goes here, not at top of linker script, so addr2line finds it last,
+       and uses it in preference to the first symbol in IRAM */
+    _iram_start = ABSOLUTE(0);
+  } > iram0_0_seg
+
+  .iram0.text :
+  {
+    /* Code marked as runnning out of IRAM */
+    _iram_text_start = ABSOLUTE(.);
+
+    mapping[iram0_text]
+    
+    INCLUDE esp32.spiram.rom-functions-iram.ld
+    _iram_text_end = ABSOLUTE(.);
+  } > iram0_0_seg
+
+  .dram0.data :
+  {
+    _data_start = ABSOLUTE(.);
+
+    mapping[dram0_data]
+
+    *(.gnu.linkonce.d.*)
+    *(.data1)
+    *(.sdata)
+    *(.sdata.*)
+    *(.gnu.linkonce.s.*)
+    *(.sdata2)
+    *(.sdata2.*)
+    *(.gnu.linkonce.s2.*)
+    *(.jcr)
+    INCLUDE esp32.spiram.rom-functions-dram.ld
+    _data_end = ABSOLUTE(.);
+    . = ALIGN(4);
+  } >dram0_0_seg
+
+  /* Shared RAM */
+  .dram0.bss (NOLOAD) :
+  {
+    . = ALIGN (8);
+    _bss_start = ABSOLUTE(.);
+
+    mapping[dram0_bss]
+
+    *(.dynsbss)
+    *(.sbss)
+    *(.sbss.*)
+    *(.gnu.linkonce.sb.*)
+    *(.scommon)
+    *(.sbss2)
+    *(.sbss2.*)
+    *(.gnu.linkonce.sb2.*)
+    *(.dynbss)
+    *(.share.mem)
+    *(.gnu.linkonce.b.*)
+
+    . = ALIGN (8);
+    _bss_end = ABSOLUTE(.);
+    _heap_start = ABSOLUTE(.);
+  } >dram0_0_seg
+
+  .flash.rodata :
+  {
+    _rodata_start = ABSOLUTE(.);
+
+    mapping[flash_rodata]
+
+    *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
+    *(.gnu.linkonce.r.*)
+    *(.rodata1)
+    __XT_EXCEPTION_TABLE_ = ABSOLUTE(.);
+    *(.xt_except_table)
+    *(.gcc_except_table .gcc_except_table.*)
+    *(.gnu.linkonce.e.*)
+    *(.gnu.version_r)
+    . = (. + 3) & ~ 3;
+    __eh_frame = ABSOLUTE(.);
+    KEEP(*(.eh_frame))
+    . = (. + 7) & ~ 3;
+    /*  C++ constructor and destructor tables, properly ordered:  */
+    __init_array_start = ABSOLUTE(.);
+    KEEP (*crtbegin.o(.ctors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+    __init_array_end = ABSOLUTE(.);
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+    /*  C++ exception handlers table:  */
+    __XT_EXCEPTION_DESCS_ = ABSOLUTE(.);
+    *(.xt_except_desc)
+    *(.gnu.linkonce.h.*)
+    __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
+    *(.xt_except_desc_end)
+    *(.dynamic)
+    *(.gnu.version_d)
+    _rodata_end = ABSOLUTE(.);
+    /* Literals are also RO data. */
+    _lit4_start = ABSOLUTE(.);
+    *(*.lit4)
+    *(.lit4.*)
+    *(.gnu.linkonce.lit4.*)
+    _lit4_end = ABSOLUTE(.);
+    . = ALIGN(4);
+    _thread_local_start = ABSOLUTE(.);
+    *(.tdata)
+    *(.tdata.*)
+    *(.tbss)
+    *(.tbss.*)
+    _thread_local_end = ABSOLUTE(.);
+    . = ALIGN(4);
+  } >drom0_0_seg
+
+  .flash.text :
+  {
+    _stext = .;
+    _text_start = ABSOLUTE(.);
+
+    mapping[flash_text]
+
+    *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
+    *(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */
+    *(.fini.literal)
+    *(.fini)
+    *(.gnu.version)
+    _text_end = ABSOLUTE(.);
+    _etext = .;
+
+    /* Similar to _iram_start, this symbol goes here so it is
+       resolved by addr2line in preference to the first symbol in
+       the flash.text segment.
+    */
+    _flash_cache_start = ABSOLUTE(0);
+  } >iram0_2_seg
+}
diff --git a/tools/ldgen/sdkconfig.py b/tools/ldgen/sdkconfig.py
new file mode 100644 (file)
index 0000000..9ef23ec
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+#
+# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import os
+from pyparsing import *
+
+import sys
+parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+kconfig_new_dir = os.path.abspath(parent_dir_name + "/kconfig_new")
+sys.path.append(kconfig_new_dir)
+import kconfiglib
+
+
+
+"""
+Encapsulates an sdkconfig file. Defines grammar of a configuration entry, and enables
+evaluation of logical expressions involving those entries.
+"""
+class SDKConfig:
+
+    # A configuration entry is in the form CONFIG=VALUE. Definitions of components of that grammar
+    IDENTIFIER = Word(printables.upper())
+
+    HEX = Combine("0x" + Word(hexnums)).setParseAction(lambda t:int(t[0], 16))
+    DECIMAL = Combine(Optional(Literal("+") | Literal("-")) + Word(nums)).setParseAction(lambda t:int(t[0]))
+    LITERAL =  Word(printables)
+    QUOTED_LITERAL = quotedString.setParseAction(removeQuotes)
+
+    VALUE = HEX | DECIMAL | LITERAL | QUOTED_LITERAL
+
+    # Operators supported by the expression evaluation
+    OPERATOR = oneOf(["=", "!=", ">", "<", "<=", ">="])
+
+    def __init__(self, kconfig_file, sdkconfig_file, env = []):
+        env = [ (name, value) for (name,value) in ( e.split("=",1) for e in env) ]
+
+        for name, value in env:
+            value = " ".join(value.split())
+            os.environ[name] = value
+
+        self.config = kconfiglib.Kconfig(kconfig_file.name)
+        self.config.load_config(sdkconfig_file.name)
+
+    def evaluate_expression(self, expression):
+        result = self.config.eval_string(expression)
+
+        if result == 0: # n
+            return False
+        elif result == 2: # y
+            return True
+        else: # m
+            raise Exception("Unsupported config expression result.")
+
+    @staticmethod
+    def get_expression_grammar():
+        identifier = SDKConfig.IDENTIFIER.setResultsName("identifier")
+        operator = SDKConfig.OPERATOR.setResultsName("operator")
+        value = SDKConfig.VALUE.setResultsName("value")
+
+        test_binary = identifier + operator + value
+        test_single = identifier
+
+        test = test_binary | test_single
+
+        condition = Group(Optional("(").suppress() + test + Optional(")").suppress())
+
+        grammar = infixNotation(
+                condition, [
+                ("!", 1, opAssoc.RIGHT),
+                ("&&", 2, opAssoc.LEFT),
+                ("||",  2, opAssoc.LEFT)])
+
+        return grammar
\ No newline at end of file
diff --git a/tools/ldgen/test/data/Kconfig b/tools/ldgen/test/data/Kconfig
new file mode 100644 (file)
index 0000000..17993d0
--- /dev/null
@@ -0,0 +1,7 @@
+menu "Test config"
+
+config PERFORMANCE_LEVEL
+   int "Performance level"
+   range 0 3
+
+endmenu
diff --git a/tools/ldgen/test/data/sample.lf b/tools/ldgen/test/data/sample.lf
new file mode 100644 (file)
index 0000000..45cb85d
--- /dev/null
@@ -0,0 +1,84 @@
+[sections:text]
+entries: 
+    .text+
+    .literal+
+    
+[sections:data]
+entries:
+    .data+
+
+[sections:bss]
+entries:
+    .bss+
+
+[sections:common]
+entries:
+    COMMON
+
+[sections:rodata]
+entries:
+    .rodata+
+
+[sections:rtc_text]
+entries:
+    .rtc.text
+    .rtc.literal
+
+[sections:rtc_data]
+entries:
+    .rtc.data
+
+[sections:rtc_rodata]
+entries:
+    .rtc.rodata
+
+[sections:rtc_bss]
+entries:
+    .rtc.bss
+
+[sections:extram_bss]
+entries:
+    .exram.bss
+
+[sections:iram]
+entries:
+    .iram+
+
+[sections:dram]
+entries:
+    .dram+
+
+[scheme:default]
+entries:
+    text -> flash_text
+    rodata -> flash_rodata
+    data -> dram0_data
+    bss -> dram0_bss
+    common -> dram0_bss
+    iram -> iram0_text
+    dram -> dram0_data
+    rtc_text -> rtc_text
+    rtc_data -> rtc_data
+    rtc_rodata -> rtc_data
+    rtc_bss -> rtc_bss
+
+[scheme:rtc]
+entries:
+    text -> rtc_text
+    data -> rtc_data
+    rodata -> rtc_data
+    bss -> rtc_bss
+    common -> rtc_bss
+
+[scheme:noflash]
+entries:
+    text -> iram0_text
+    rodata -> dram0_data
+
+[scheme:noflash_text]
+entries:
+    text -> iram0_text
+
+[scheme:noflash_data]
+entries:
+    rodata -> dram0_data
\ No newline at end of file
diff --git a/tools/ldgen/test/data/sdkconfig b/tools/ldgen/test/data/sdkconfig
new file mode 100644 (file)
index 0000000..b9e025f
--- /dev/null
@@ -0,0 +1,8 @@
+CONFIG_TEST_STRING="This Is~AString#$^#$&^(*&^#(*&^)(*@_)(#*_)(*_(*}Value"
+CONFIG_TEST_NON_STRING=y
+CONFIG_TEST_WHITESPACE=      y
+CONFIG_TEST_EMPTY=
+CONFIG_TEST_POSITIVE_INT=110
+CONFIG_TEST_HEX_INT=0x8000
+CONFIG_TEST_NEGATIVE_INT=-9
+CONFIG_PERFORMANCE_LEVEL=0
\ No newline at end of file
diff --git a/tools/ldgen/test/data/sections.info b/tools/ldgen/test/data/sections.info
new file mode 100644 (file)
index 0000000..db450cf
--- /dev/null
@@ -0,0 +1,1626 @@
+In archive libfreertos.a:
+
+FreeRTOS-openocd.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .text         00000000  00000000  00000000  00000034  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  1 .data         00000000  00000000  00000000  00000034  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  2 .bss          00000000  00000000  00000000  00000034  2**0
+                  ALLOC
+  3 .dram1        00000004  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+  4 .debug_info   0000008f  00000000  00000000  00000038  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  5 .debug_abbrev 0000003e  00000000  00000000  000000c7  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  6 .debug_aranges 00000018  00000000  00000000  00000105  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  7 .debug_line   0000005e  00000000  00000000  0000011d  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  8 .debug_str    0000018d  00000000  00000000  0000017b  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  9 .comment      0000003b  00000000  00000000  00000308  2**0
+                  CONTENTS, READONLY
+ 10 .xtensa.info  00000038  00000000  00000000  00000343  2**0
+                  CONTENTS, READONLY
+ 11 .xt.prop      0000000c  00000000  00000000  0000037b  2**0
+                  CONTENTS, RELOC, READONLY
+
+croutine.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.prvCheckPendingReadyList 00000018  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.prvCheckDelayedList 0000002c  00000000  00000000  0000004c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.xCoRoutineCreate 0000001c  00000000  00000000  00000078  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.vCoRoutineAddToDelayedList 00000020  00000000  00000000  00000094  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.vCoRoutineSchedule 00000014  00000000  00000000  000000b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.xCoRoutineRemoveFromEventList 00000010  00000000  00000000  000000c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .text         00000000  00000000  00000000  000000d8  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  7 .data         00000000  00000000  00000000  000000d8  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  8 .bss          00000000  00000000  00000000  000000d8  2**0
+                  ALLOC
+  9 .text.prvCheckPendingReadyList 00000056  00000000  00000000  000000d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .text.prvCheckDelayedList 000000ac  00000000  00000000  00000130  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .rodata.str1.4 00000074  00000000  00000000  000001dc  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 12 .text.xCoRoutineCreate 00000028  00000000  00000000  00000250  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .text.vCoRoutineAddToDelayedList 00000056  00000000  00000000  00000278  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .text.vCoRoutineSchedule 0000007a  00000000  00000000  000002d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 15 .text.xCoRoutineRemoveFromEventList 00000031  00000000  00000000  0000034c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .rodata.__FUNCTION__$5025 00000011  00000000  00000000  00000380  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 17 .bss.xPassedTicks 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 18 .bss.xLastTickCount 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 19 .bss.xCoRoutineTickCount 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 20 .bss.uxTopCoRoutineReadyPriority 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 21 .bss.pxCurrentCoRoutine 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 22 .bss.xPendingReadyCoRoutineList 00000014  00000000  00000000  00000394  2**2
+                  ALLOC
+ 23 .bss.pxOverflowDelayedCoRoutineList 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 24 .bss.pxDelayedCoRoutineList 00000004  00000000  00000000  00000394  2**2
+                  ALLOC
+ 25 .bss.pxReadyCoRoutineLists 00000028  00000000  00000000  00000394  2**2
+                  ALLOC
+ 26 .debug_frame  000000a0  00000000  00000000  00000394  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 27 .debug_info   000006b8  00000000  00000000  00000434  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 28 .debug_abbrev 00000233  00000000  00000000  00000aec  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 29 .debug_loc    0000013b  00000000  00000000  00000d1f  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 30 .debug_aranges 00000048  00000000  00000000  00000e5a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 31 .debug_ranges 00000038  00000000  00000000  00000ea2  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 32 .debug_line   0000037e  00000000  00000000  00000eda  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 33 .debug_str    00000533  00000000  00000000  00001258  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 34 .comment      0000003b  00000000  00000000  0000178b  2**0
+                  CONTENTS, READONLY
+ 35 .xtensa.info  00000038  00000000  00000000  000017c6  2**0
+                  CONTENTS, READONLY
+ 36 .xt.lit       00000030  00000000  00000000  000017fe  2**0
+                  CONTENTS, RELOC, READONLY
+ 37 .xt.prop      00000270  00000000  00000000  0000182e  2**0
+                  CONTENTS, RELOC, READONLY
+
+event_groups.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.xEventGroupCreate 0000000c  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.xEventGroupWaitBits 0000006c  00000000  00000000  00000040  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.xEventGroupClearBits 00000028  00000000  00000000  000000ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.xEventGroupGetBitsFromISR 00000004  00000000  00000000  000000d4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.xEventGroupSetBits 0000003c  00000000  00000000  000000d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.xEventGroupSync 0000005c  00000000  00000000  00000114  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .literal.vEventGroupDelete 00000030  00000000  00000000  00000170  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.vEventGroupSetBitsCallback 00000004  00000000  00000000  000001a0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.vEventGroupClearBitsCallback 00000004  00000000  00000000  000001a4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .text         00000000  00000000  00000000  000001a8  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 10 .data         00000000  00000000  00000000  000001a8  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 11 .bss          00000000  00000000  00000000  000001a8  2**0
+                  ALLOC
+ 12 .text.prvTestWaitCondition 0000001e  00000000  00000000  000001a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .text.xEventGroupCreate 00000027  00000000  00000000  000001c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .rodata.str1.4 00000058  00000000  00000000  000001f0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 15 .text.xEventGroupWaitBits 00000140  00000000  00000000  00000248  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .text.xEventGroupClearBits 00000060  00000000  00000000  00000388  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .text.xEventGroupGetBitsFromISR 00000010  00000000  00000000  000003e8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .text.xEventGroupSetBits 000000c3  00000000  00000000  000003f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .text.xEventGroupSync 00000108  00000000  00000000  000004bc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .text.vEventGroupDelete 0000005d  00000000  00000000  000005c4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .text.vEventGroupSetBitsCallback 0000000f  00000000  00000000  00000624  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 22 .text.vEventGroupClearBitsCallback 0000000f  00000000  00000000  00000634  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .rodata.__FUNCTION__$5129 00000012  00000000  00000000  00000644  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 24 .rodata.__FUNCTION__$5120 00000013  00000000  00000000  00000658  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 25 .rodata.__FUNCTION__$5100 00000015  00000000  00000000  0000066c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 26 .rodata.__FUNCTION__$5092 00000014  00000000  00000000  00000684  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 27 .rodata.__FUNCTION__$5078 00000010  00000000  00000000  00000698  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 28 .debug_frame  00000100  00000000  00000000  000006a8  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 29 .debug_info   00000e02  00000000  00000000  000007a8  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 30 .debug_abbrev 00000236  00000000  00000000  000015aa  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 31 .debug_loc    00000546  00000000  00000000  000017e0  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 32 .debug_aranges 00000068  00000000  00000000  00001d26  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 33 .debug_ranges 00000070  00000000  00000000  00001d8e  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 34 .debug_line   000006af  00000000  00000000  00001dfe  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 35 .debug_str    00000677  00000000  00000000  000024ad  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 36 .comment      0000003b  00000000  00000000  00002b24  2**0
+                  CONTENTS, READONLY
+ 37 .xtensa.info  00000038  00000000  00000000  00002b5f  2**0
+                  CONTENTS, READONLY
+ 38 .xt.lit       00000048  00000000  00000000  00002b97  2**0
+                  CONTENTS, RELOC, READONLY
+ 39 .xt.prop      00000468  00000000  00000000  00002bdf  2**0
+                  CONTENTS, RELOC, READONLY
+
+list.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .text         00000000  00000000  00000000  00000034  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  1 .data         00000000  00000000  00000000  00000034  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  2 .bss          00000000  00000000  00000000  00000034  2**0
+                  ALLOC
+  3 .text.vListInitialise 00000015  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  4 .text.vListInitialiseItem 00000009  00000000  00000000  0000004c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  5 .text.vListInsertEnd 0000001b  00000000  00000000  00000058  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  6 .text.vListInsert 0000002f  00000000  00000000  00000074  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .text.uxListRemove 00000026  00000000  00000000  000000a4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .debug_frame  00000088  00000000  00000000  000000cc  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  9 .debug_info   000002a1  00000000  00000000  00000154  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 10 .debug_abbrev 000000dc  00000000  00000000  000003f5  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 11 .debug_loc    00000081  00000000  00000000  000004d1  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 12 .debug_aranges 00000040  00000000  00000000  00000552  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 13 .debug_ranges 00000030  00000000  00000000  00000592  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 14 .debug_line   0000024c  00000000  00000000  000005c2  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 15 .debug_str    000002c2  00000000  00000000  0000080e  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 16 .comment      0000003b  00000000  00000000  00000ad0  2**0
+                  CONTENTS, READONLY
+ 17 .xtensa.info  00000038  00000000  00000000  00000b0b  2**0
+                  CONTENTS, READONLY
+ 18 .xt.prop      000000f0  00000000  00000000  00000b43  2**0
+                  CONTENTS, RELOC, READONLY
+
+port.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.pxPortInitialiseStack 00000018  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.xPortStartScheduler 00000014  00000000  00000000  0000004c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.xPortSysTickHandler 00000008  00000000  00000000  00000060  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.vPortYieldOtherCore 00000004  00000000  00000000  00000068  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.vPortReleaseTaskMPUSettings 00000004  00000000  00000000  0000006c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.xPortInIsrContext 00000008  00000000  00000000  00000070  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .iram1.literal 00000004  00000000  00000000  00000078  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.vPortAssertIfInISR 00000018  00000000  00000000  0000007c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.vPortCPUInitializeMutex 00000004  00000000  00000000  00000094  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  9 .literal.vPortCPUAcquireMutex 00000030  00000000  00000000  00000098  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .literal.vPortCPUAcquireMutexTimeout 00000030  00000000  00000000  000000c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .literal.vPortCPUReleaseMutex 00000028  00000000  00000000  000000f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 12 .literal.vPortSetStackWatchpoint 00000008  00000000  00000000  00000120  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .text         00000000  00000000  00000000  00000128  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 14 .data         00000000  00000000  00000000  00000128  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 15 .bss          00000000  00000000  00000000  00000128  2**0
+                  ALLOC
+ 16 .text.pxPortInitialiseStack 00000086  00000000  00000000  00000128  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .text.vPortEndScheduler 00000005  00000000  00000000  000001b0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 18 .text.xPortStartScheduler 0000002e  00000000  00000000  000001b8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .text.xPortSysTickHandler 00000016  00000000  00000000  000001e8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .text.vPortYieldOtherCore 0000000e  00000000  00000000  00000200  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .text.vPortStoreTaskMPUSettings 00000013  00000000  00000000  00000210  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 22 .text.vPortReleaseTaskMPUSettings 0000000e  00000000  00000000  00000224  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .text.xPortInIsrContext 00000026  00000000  00000000  00000234  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .iram1        0000001a  00000000  00000000  0000025c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 25 .rodata.str1.4 0000013b  00000000  00000000  00000278  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 26 .text.vPortAssertIfInISR 00000025  00000000  00000000  000003b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 27 .text.vPortCPUInitializeMutex 0000000e  00000000  00000000  000003dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 28 .text.vPortCPUAcquireMutex 00000088  00000000  00000000  000003ec  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 29 .text.vPortCPUAcquireMutexTimeout 000000ab  00000000  00000000  00000474  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 30 .text.vPortCPUReleaseMutex 00000061  00000000  00000000  00000520  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 31 .text.vPortSetStackWatchpoint 0000001a  00000000  00000000  00000584  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 32 .text.xPortGetTickRateHz 00000008  00000000  00000000  000005a0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 33 .rodata.__func__$5264 00000029  00000000  00000000  000005a8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 34 .rodata.__func__$5259 00000029  00000000  00000000  000005d4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 35 .rodata.__FUNCTION__$5243 00000013  00000000  00000000  00000600  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 36 .bss.port_interruptNesting 00000008  00000000  00000000  00000614  2**2
+                  ALLOC
+ 37 .bss.port_xSchedulerRunning 00000008  00000000  00000000  00000614  2**2
+                  ALLOC
+ 38 .debug_frame  00000190  00000000  00000000  00000614  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 39 .debug_info   00000e78  00000000  00000000  000007a4  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 40 .debug_abbrev 00000404  00000000  00000000  0000161c  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 41 .debug_loc    000005f1  00000000  00000000  00001a20  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 42 .debug_aranges 00000098  00000000  00000000  00002011  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 43 .debug_ranges 000000a0  00000000  00000000  000020a9  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 44 .debug_line   000005fb  00000000  00000000  00002149  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 45 .debug_str    0000071f  00000000  00000000  00002744  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 46 .comment      0000003b  00000000  00000000  00002e63  2**0
+                  CONTENTS, READONLY
+ 47 .xtensa.info  00000038  00000000  00000000  00002e9e  2**0
+                  CONTENTS, READONLY
+ 48 .xt.lit       00000068  00000000  00000000  00002ed6  2**0
+                  CONTENTS, RELOC, READONLY
+ 49 .xt.prop      00000408  00000000  00000000  00002f3e  2**0
+                  CONTENTS, RELOC, READONLY
+
+portasm.S.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal      00000074  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .text         000001e0  00000000  00000000  000000a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .data         00000c0c  00000000  00000000  00000290  2**4
+                  CONTENTS, ALLOC, LOAD, DATA
+  3 .bss          00000000  00000000  00000000  00000e9c  2**0
+                  ALLOC
+  4 .xtensa.info  00000038  00000000  00000000  00000e9c  2**0
+                  CONTENTS, READONLY
+  5 .debug_line   00000432  00000000  00000000  00000ed4  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  6 .debug_info   00000093  00000000  00000000  00001306  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  7 .debug_abbrev 00000014  00000000  00000000  00001399  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  8 .debug_aranges 00000020  00000000  00000000  000013b0  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  9 .xt.lit       00000008  00000000  00000000  000013d0  2**0
+                  CONTENTS, RELOC, READONLY
+ 10 .xt.prop      00000168  00000000  00000000  000013d8  2**0
+                  CONTENTS, RELOC, READONLY
+
+queue.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.prvIsQueueFull 00000008  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.prvCopyDataToQueue 0000000c  00000000  00000000  0000003c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.prvNotifyQueueSetContainer 0000002c  00000000  00000000  00000048  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.prvCopyDataFromQueue 00000004  00000000  00000000  00000074  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.xQueueGenericReset 00000030  00000000  00000000  00000078  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.prvInitialiseNewQueue 00000004  00000000  00000000  000000a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .literal.xQueueGenericCreate 0000001c  00000000  00000000  000000ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.xQueueGetMutexHolder 00000008  00000000  00000000  000000c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.xQueueCreateCountingSemaphore 00000028  00000000  00000000  000000d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .literal.xQueueGenericSend 0000007c  00000000  00000000  000000f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .literal.prvInitialiseMutex 00000008  00000000  00000000  00000174  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .literal.xQueueCreateMutex 00000008  00000000  00000000  0000017c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 12 .literal.xQueueGiveMutexRecursive 0000001c  00000000  00000000  00000184  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .literal.xQueueGenericSendFromISR 0000003c  00000000  00000000  000001a0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .literal.xQueueGiveFromISR 00000030  00000000  00000000  000001dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 15 .literal.xQueueGenericReceive 00000078  00000000  00000000  0000020c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .literal.xQueueTakeMutexRecursive 0000001c  00000000  00000000  00000284  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .literal.xQueueReceiveFromISR 00000030  00000000  00000000  000002a0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .literal.xQueuePeekFromISR 00000034  00000000  00000000  000002d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .literal.uxQueueMessagesWaiting 0000001c  00000000  00000000  00000304  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .literal.uxQueueSpacesAvailable 0000001c  00000000  00000000  00000320  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .literal.uxQueueMessagesWaitingFromISR 0000001c  00000000  00000000  0000033c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 22 .literal.vQueueDelete 00000018  00000000  00000000  00000358  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .literal.xQueueIsQueueEmptyFromISR 0000001c  00000000  00000000  00000370  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .literal.xQueueIsQueueFullFromISR 0000001c  00000000  00000000  0000038c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 25 .literal.vQueueWaitForMessageRestricted 0000000c  00000000  00000000  000003a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 26 .literal.xQueueCreateSet 00000004  00000000  00000000  000003b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 27 .literal.xQueueSelectFromSet 00000004  00000000  00000000  000003b8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 28 .literal.xQueueSelectFromSetFromISR 00000004  00000000  00000000  000003bc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 29 .text         00000000  00000000  00000000  000003c0  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 30 .data         00000000  00000000  00000000  000003c0  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 31 .bss          00000000  00000000  00000000  000003c0  2**0
+                  ALLOC
+ 32 .text.prvIsQueueEmpty 00000012  00000000  00000000  000003c0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 33 .text.prvIsQueueFull 0000002a  00000000  00000000  000003d4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 34 .text.prvCopyDataToQueue 0000009e  00000000  00000000  00000400  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 35 .rodata.str1.4 00000050  00000000  00000000  000004a0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 36 .text.prvNotifyQueueSetContainer 00000076  00000000  00000000  000004f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 37 .text.prvCopyDataFromQueue 00000024  00000000  00000000  00000568  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 38 .text.xQueueGenericReset 00000096  00000000  00000000  0000058c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 39 .text.prvInitialiseNewQueue 00000023  00000000  00000000  00000624  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 40 .text.xQueueGenericCreate 0000004d  00000000  00000000  00000648  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 41 .text.xQueueGetMutexHolder 00000023  00000000  00000000  00000698  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 42 .text.xQueueCreateCountingSemaphore 0000006a  00000000  00000000  000006bc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 43 .text.xQueueGenericSend 0000018c  00000000  00000000  00000728  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 44 .text.prvInitialiseMutex 00000026  00000000  00000000  000008b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 45 .text.xQueueCreateMutex 0000001a  00000000  00000000  000008dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 46 .text.xQueueGiveMutexRecursive 0000004c  00000000  00000000  000008f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 47 .text.xQueueGenericSendFromISR 000000e6  00000000  00000000  00000944  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 48 .text.xQueueGiveFromISR 000000c2  00000000  00000000  00000a2c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 49 .text.xQueueGenericReceive 00000178  00000000  00000000  00000af0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 50 .text.xQueueTakeMutexRecursive 00000051  00000000  00000000  00000c68  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 51 .text.xQueueReceiveFromISR 000000a6  00000000  00000000  00000cbc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 52 .text.xQueuePeekFromISR 00000098  00000000  00000000  00000d64  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 53 .text.uxQueueMessagesWaiting 00000038  00000000  00000000  00000dfc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 54 .text.uxQueueSpacesAvailable 0000003e  00000000  00000000  00000e34  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 55 .text.uxQueueMessagesWaitingFromISR 00000038  00000000  00000000  00000e74  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 56 .text.vQueueDelete 00000028  00000000  00000000  00000eac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 57 .text.xQueueIsQueueEmptyFromISR 00000042  00000000  00000000  00000ed4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 58 .text.xQueueIsQueueFullFromISR 00000044  00000000  00000000  00000f18  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 59 .text.vQueueWaitForMessageRestricted 0000002a  00000000  00000000  00000f5c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 60 .text.xQueueCreateSet 00000014  00000000  00000000  00000f88  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 61 .text.xQueueAddToSet 0000001e  00000000  00000000  00000f9c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 62 .text.xQueueRemoveFromSet 00000020  00000000  00000000  00000fbc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 63 .text.xQueueSelectFromSet 00000018  00000000  00000000  00000fdc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 64 .text.xQueueSelectFromSetFromISR 00000015  00000000  00000000  00000ff4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 65 .rodata.__FUNCTION__$5459 00000019  00000000  00000000  0000100c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 66 .rodata.__FUNCTION__$5449 0000001a  00000000  00000000  00001028  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 67 .rodata.__FUNCTION__$5429 0000000d  00000000  00000000  00001044  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 68 .rodata.__FUNCTION__$5424 0000001e  00000000  00000000  00001054  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 69 .rodata.__FUNCTION__$5418 00000017  00000000  00000000  00001074  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 70 .rodata.__FUNCTION__$5412 00000017  00000000  00000000  0000108c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 71 .rodata.__FUNCTION__$5406 00000012  00000000  00000000  000010a4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 72 .rodata.__FUNCTION__$5397 00000015  00000000  00000000  000010b8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 73 .rodata.__FUNCTION__$5387 00000015  00000000  00000000  000010d0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 74 .rodata.__FUNCTION__$5376 00000012  00000000  00000000  000010e8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 75 .rodata.__FUNCTION__$5368 00000019  00000000  00000000  000010fc  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 76 .rodata.__FUNCTION__$5495 0000001b  00000000  00000000  00001118  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 77 .rodata.__FUNCTION__$5357 00000012  00000000  00000000  00001134  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 78 .rodata.__FUNCTION__$5346 0000001e  00000000  00000000  00001148  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 79 .rodata.__FUNCTION__$5340 00000019  00000000  00000000  00001168  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 80 .rodata.__FUNCTION__$5333 00000019  00000000  00000000  00001184  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 81 .rodata.__FUNCTION__$5306 00000014  00000000  00000000  000011a0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 82 .rodata.__FUNCTION__$5297 00000013  00000000  00000000  000011b4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 83 .debug_frame  00000310  00000000  00000000  000011c8  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 84 .debug_info   0000226e  00000000  00000000  000014d8  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 85 .debug_abbrev 00000247  00000000  00000000  00003746  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 86 .debug_loc    000010f0  00000000  00000000  0000398d  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 87 .debug_aranges 00000118  00000000  00000000  00004a7d  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 88 .debug_ranges 00000108  00000000  00000000  00004b95  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 89 .debug_line   00000e69  00000000  00000000  00004c9d  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 90 .debug_str    000009f1  00000000  00000000  00005b06  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 91 .comment      0000003b  00000000  00000000  000064f7  2**0
+                  CONTENTS, READONLY
+ 92 .xtensa.info  00000038  00000000  00000000  00006532  2**0
+                  CONTENTS, READONLY
+ 93 .xt.lit       000000e8  00000000  00000000  0000656a  2**0
+                  CONTENTS, RELOC, READONLY
+ 94 .xt.prop      00000e10  00000000  00000000  00006652  2**0
+                  CONTENTS, RELOC, READONLY
+
+ringbuf.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.returnItemToRingbufBytebuf 0000001c  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.returnItemToRingbufDefault 00000044  00000000  00000000  00000050  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.getItemFromRingbufDefault 00000024  00000000  00000000  00000094  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.copyItemToRingbufNoSplit 00000024  00000000  00000000  000000b8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.copyItemToRingbufByteBuf 00000008  00000000  00000000  000000dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.copyItemToRingbufAllowSplit 00000034  00000000  00000000  000000e4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .literal.xRingbufferReceiveGeneric 00000020  00000000  00000000  00000118  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.xRingbufferPrintInfo 00000020  00000000  00000000  00000138  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.xRingbufferGetCurFreeSize 0000001c  00000000  00000000  00000158  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .literal.xRingbufferCreate 00000064  00000000  00000000  00000174  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .literal.xRingbufferCreateNoSplit 00000004  00000000  00000000  000001d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .literal.vRingbufferDelete 00000010  00000000  00000000  000001dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 12 .literal.xRingbufferGetMaxItemSize 00000014  00000000  00000000  000001ec  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .literal.xRingbufferIsNextItemWrapped 00000014  00000000  00000000  00000200  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .literal.xRingbufferSend 0000003c  00000000  00000000  00000214  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 15 .literal.xRingbufferSendFromISR 00000024  00000000  00000000  00000250  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .literal.xRingbufferReceive 00000004  00000000  00000000  00000274  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .literal.xRingbufferReceiveFromISR 0000001c  00000000  00000000  00000278  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .literal.xRingbufferReceiveUpTo 00000020  00000000  00000000  00000294  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .literal.xRingbufferReceiveUpToFromISR 00000024  00000000  00000000  000002b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .literal.vRingbufferReturnItem 0000000c  00000000  00000000  000002d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .literal.vRingbufferReturnItemFromISR 0000000c  00000000  00000000  000002e4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 22 .literal.xRingbufferAddToQueueSetRead 00000018  00000000  00000000  000002f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .literal.xRingbufferAddToQueueSetWrite 00000018  00000000  00000000  00000308  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .literal.xRingbufferRemoveFromQueueSetRead 00000018  00000000  00000000  00000320  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 25 .literal.xRingbufferRemoveFromQueueSetWrite 00000018  00000000  00000000  00000338  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 26 .text         00000000  00000000  00000000  00000350  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 27 .data         00000000  00000000  00000000  00000350  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 28 .bss          00000000  00000000  00000000  00000350  2**0
+                  ALLOC
+ 29 .text.ringbufferFreeMem 00000015  00000000  00000000  00000350  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 30 .text.getItemFromRingbufByteBuf 00000056  00000000  00000000  00000368  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 31 .text.getCurFreeSizeByteBuf 00000015  00000000  00000000  000003c0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 32 .text.getCurFreeSizeAllowSplit 00000030  00000000  00000000  000003d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 33 .text.getCurFreeSizeNoSplit 00000024  00000000  00000000  00000408  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 34 .rodata.str1.4 00000083  00000000  00000000  0000042c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 35 .text.returnItemToRingbufBytebuf 00000045  00000000  00000000  000004b0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 36 .text.returnItemToRingbufDefault 00000136  00000000  00000000  000004f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 37 .text.getItemFromRingbufDefault 000000a5  00000000  00000000  00000630  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 38 .text.copyItemToRingbufNoSplit 000000b0  00000000  00000000  000006d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 39 .text.copyItemToRingbufByteBuf 00000046  00000000  00000000  00000788  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 40 .text.copyItemToRingbufAllowSplit 00000124  00000000  00000000  000007d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 41 .text.xRingbufferReceiveGeneric 00000070  00000000  00000000  000008f4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 42 .text.xRingbufferPrintInfo 00000046  00000000  00000000  00000964  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 43 .text.xRingbufferGetCurFreeSize 00000043  00000000  00000000  000009ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 44 .text.xRingbufferCreate 00000118  00000000  00000000  000009f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 45 .text.xRingbufferCreateNoSplit 0000001c  00000000  00000000  00000b08  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 46 .text.vRingbufferDelete 0000002c  00000000  00000000  00000b24  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 47 .text.xRingbufferGetMaxItemSize 00000022  00000000  00000000  00000b50  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 48 .text.xRingbufferIsNextItemWrapped 00000030  00000000  00000000  00000b74  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 49 .text.xRingbufferSend 000000d4  00000000  00000000  00000ba4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 50 .text.xRingbufferSendFromISR 00000062  00000000  00000000  00000c78  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 51 .text.xRingbufferReceive 00000015  00000000  00000000  00000cdc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 52 .text.xRingbufferReceiveFromISR 00000040  00000000  00000000  00000cf4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 53 .text.xRingbufferReceiveUpTo 00000054  00000000  00000000  00000d34  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 54 .text.xRingbufferReceiveUpToFromISR 00000064  00000000  00000000  00000d88  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 55 .text.vRingbufferReturnItem 00000030  00000000  00000000  00000dec  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 56 .text.vRingbufferReturnItemFromISR 0000002c  00000000  00000000  00000e1c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 57 .text.xRingbufferAddToQueueSetRead 0000002c  00000000  00000000  00000e48  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 58 .text.xRingbufferAddToQueueSetWrite 0000002c  00000000  00000000  00000e74  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 59 .text.xRingbufferRemoveFromQueueSetRead 0000002c  00000000  00000000  00000ea0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 60 .text.xRingbufferRemoveFromQueueSetWrite 0000002c  00000000  00000000  00000ecc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 61 .rodata.__FUNCTION__$5577 00000023  00000000  00000000  00000ef8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 62 .rodata.__FUNCTION__$5571 00000022  00000000  00000000  00000f1c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 63 .rodata.__FUNCTION__$5565 0000001e  00000000  00000000  00000f40  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 64 .rodata.__FUNCTION__$5559 0000001d  00000000  00000000  00000f60  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 65 .rodata.__FUNCTION__$5542 0000001e  00000000  00000000  00000f80  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 66 .rodata.__FUNCTION__$5534 00000017  00000000  00000000  00000fa0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 67 .rodata.__FUNCTION__$5527 0000001a  00000000  00000000  00000fb8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 68 .rodata.__FUNCTION__$5508 0000001a  00000000  00000000  00000fd4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 69 .rodata.__FUNCTION__$5497 00000017  00000000  00000000  00000ff0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 70 .rodata.__FUNCTION__$5482 00000010  00000000  00000000  00001008  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 71 .rodata.__FUNCTION__$5469 0000001d  00000000  00000000  00001018  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 72 .rodata.__FUNCTION__$5464 0000001a  00000000  00000000  00001038  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 73 .rodata.__FUNCTION__$5379 0000001c  00000000  00000000  00001054  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 74 .rodata.__FUNCTION__$5395 0000001a  00000000  00000000  00001070  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 75 .rodata.__FUNCTION__$5408 0000001b  00000000  00000000  0000108c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 76 .rodata.__FUNCTION__$5418 0000001b  00000000  00000000  000010a8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 77 .rodata.__FUNCTION__$5369 00000019  00000000  00000000  000010c4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 78 .rodata.__FUNCTION__$5450 00000012  00000000  00000000  000010e0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 79 .rodata.__FUNCTION__$5428 0000001a  00000000  00000000  000010f4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 80 .rodata.__FUNCTION__$5423 00000015  00000000  00000000  00001110  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 81 .debug_frame  000002f8  00000000  00000000  00001128  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 82 .debug_info   00001dc4  00000000  00000000  00001420  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 83 .debug_abbrev 000002c9  00000000  00000000  000031e4  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 84 .debug_loc    00000df4  00000000  00000000  000034ad  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 85 .debug_aranges 00000110  00000000  00000000  000042a1  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 86 .debug_ranges 00000130  00000000  00000000  000043b1  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 87 .debug_line   00000ce4  00000000  00000000  000044e1  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 88 .debug_str    000008b1  00000000  00000000  000051c5  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 89 .comment      0000003b  00000000  00000000  00005a76  2**0
+                  CONTENTS, READONLY
+ 90 .xtensa.info  00000038  00000000  00000000  00005ab1  2**0
+                  CONTENTS, READONLY
+ 91 .xt.lit       000000d0  00000000  00000000  00005ae9  2**0
+                  CONTENTS, RELOC, READONLY
+ 92 .xt.prop      00000bb8  00000000  00000000  00005bb9  2**0
+                  CONTENTS, RELOC, READONLY
+
+tasks.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.prvResetNextTaskUnblockTime 00000008  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.prvTaskGetSnapshotsFromList 00000004  00000000  00000000  0000003c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.prvDeleteTLS 00000018  00000000  00000000  00000040  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.prvTaskIsTaskSuspended 0000001c  00000000  00000000  00000058  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.prvInitialiseNewTask 00000018  00000000  00000000  00000074  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.prvInitialiseTaskLists 00000040  00000000  00000000  0000008c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .literal.prvDeleteTCB 0000002c  00000000  00000000  000000cc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.prvAddCurrentTaskToDelayedList 0000001c  00000000  00000000  000000f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.taskYIELD_OTHER_CORE 00000010  00000000  00000000  00000114  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .literal.vTaskEndScheduler 00000008  00000000  00000000  00000124  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .literal.vTaskSuspendAll 00000008  00000000  00000000  0000012c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .literal.uxTaskGetNumberOfTasks 00000004  00000000  00000000  00000134  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 12 .literal.xTaskGetIdleTaskHandle 0000001c  00000000  00000000  00000138  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .literal.xTaskGetIdleTaskHandleForCPU 0000001c  00000000  00000000  00000154  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .literal.vTaskSwitchContext 0000003c  00000000  00000000  00000170  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 15 .literal.vTaskSetTimeOutState 00000020  00000000  00000000  000001ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .literal.vTaskMissedYield 00000004  00000000  00000000  000001cc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .literal.vTaskAllocateMPURegions 00000020  00000000  00000000  000001d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .literal.xTaskGetCurrentTaskHandle 00000008  00000000  00000000  000001f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .literal.__getreent 00000008  00000000  00000000  000001f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .literal.pcTaskGetTaskName 0000001c  00000000  00000000  00000200  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .literal.pvTaskGetThreadLocalStoragePointer 00000004  00000000  00000000  0000021c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 22 .literal.xTaskGetAffinity 00000004  00000000  00000000  00000220  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .literal.uxTaskGetStackHighWaterMark 00000008  00000000  00000000  00000224  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .literal.pxTaskGetStackStart 00000004  00000000  00000000  0000022c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 25 .literal.xTaskGetCurrentTaskHandleForCPU 00000004  00000000  00000000  00000230  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 26 .literal.xTaskGetSchedulerState 0000000c  00000000  00000000  00000234  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 27 .literal.vTaskEnterCritical 00000034  00000000  00000000  00000240  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 28 .literal.vTaskExitCritical 00000030  00000000  00000000  00000274  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 29 .literal.prvAddNewTaskToReadyList 00000058  00000000  00000000  000002a4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 30 .literal.xTaskCreateRestricted 00000028  00000000  00000000  000002fc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 31 .literal.xTaskCreatePinnedToCore 00000018  00000000  00000000  00000324  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 32 .literal.vTaskStartScheduler 00000038  00000000  00000000  0000033c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 33 .literal.vTaskDelete 00000060  00000000  00000000  00000374  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 34 .literal.vTaskDelayUntil 00000048  00000000  00000000  000003d4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 35 .literal.vTaskDelay 00000038  00000000  00000000  0000041c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 36 .literal.eTaskGetState 00000038  00000000  00000000  00000454  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 37 .literal.uxTaskPriorityGet 00000010  00000000  00000000  0000048c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 38 .literal.uxTaskPriorityGetFromISR 00000010  00000000  00000000  0000049c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 39 .literal.vTaskPrioritySet 00000044  00000000  00000000  000004ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 40 .literal.vTaskSuspend 00000060  00000000  00000000  000004f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 41 .literal.vTaskResume 00000044  00000000  00000000  00000550  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 42 .literal.xTaskResumeFromISR 0000004c  00000000  00000000  00000594  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 43 .literal.prvCheckTasksWaitingTermination 00000030  00000000  00000000  000005e0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 44 .literal.prvIdleTask 00000008  00000000  00000000  00000610  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 45 .literal.xTaskGetTickCount 00000010  00000000  00000000  00000618  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 46 .literal.xTaskGetTickCountFromISR 00000010  00000000  00000000  00000628  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 47 .literal.xTaskIncrementTick 00000078  00000000  00000000  00000638  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 48 .literal.xTaskResumeAll 0000005c  00000000  00000000  000006b0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 49 .literal.vTaskPlaceOnEventList 00000040  00000000  00000000  0000070c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 50 .literal.vTaskPlaceOnUnorderedEventList 00000054  00000000  00000000  0000074c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 51 .literal.vTaskPlaceOnEventListRestricted 00000038  00000000  00000000  000007a0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 52 .literal.xTaskRemoveFromEventList 00000058  00000000  00000000  000007d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 53 .literal.xTaskRemoveFromUnorderedEventList 0000005c  00000000  00000000  00000830  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 54 .literal.xTaskCheckForTimeOut 0000003c  00000000  00000000  0000088c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 55 .literal.vTaskSetThreadLocalStoragePointerAndDelCallback 00000010  00000000  00000000  000008c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 56 .literal.vTaskSetThreadLocalStoragePointer 00000004  00000000  00000000  000008d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 57 .literal.vTaskPriorityInherit 0000002c  00000000  00000000  000008dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 58 .literal.xTaskPriorityDisinherit 00000040  00000000  00000000  00000908  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 59 .literal.uxTaskResetEventItemValue 00000010  00000000  00000000  00000948  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 60 .literal.pvTaskIncrementMutexHeldCount 00000010  00000000  00000000  00000958  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 61 .literal.ulTaskNotifyTake 00000030  00000000  00000000  00000968  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 62 .literal.xTaskNotifyWait 00000030  00000000  00000000  00000998  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 63 .literal.xTaskNotify 00000050  00000000  00000000  000009c8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 64 .literal.xTaskNotifyFromISR 00000058  00000000  00000000  00000a18  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 65 .literal.vTaskNotifyGiveFromISR 00000058  00000000  00000000  00000a70  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 66 .literal.uxTaskGetSnapshotAll 00000030  00000000  00000000  00000ac8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 67 .text         00000000  00000000  00000000  00000af8  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 68 .data         00000000  00000000  00000000  00000af8  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 69 .bss          00000000  00000000  00000000  00000af8  2**0
+                  ALLOC
+ 70 .text.prvTaskCheckFreeStackSpace 00000019  00000000  00000000  00000af8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 71 .text.prvResetNextTaskUnblockTime 00000034  00000000  00000000  00000b14  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 72 .text.prvTaskGetSnapshot 00000036  00000000  00000000  00000b48  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 73 .text.prvTaskGetSnapshotsFromList 00000047  00000000  00000000  00000b80  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 74 .rodata.str1.4 00000161  00000000  00000000  00000bc8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 75 .text.prvDeleteTLS 00000040  00000000  00000000  00000d2c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 76 .text.prvTaskIsTaskSuspended 0000004e  00000000  00000000  00000d6c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 77 .text.prvInitialiseNewTask 000000d0  00000000  00000000  00000dbc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 78 .text.prvInitialiseTaskLists 0000006e  00000000  00000000  00000e8c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 79 .text.prvDeleteTCB 0000005d  00000000  00000000  00000efc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 80 .text.prvAddCurrentTaskToDelayedList 0000006a  00000000  00000000  00000f5c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 81 .text.taskYIELD_OTHER_CORE 00000053  00000000  00000000  00000fc8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 82 .text.vTaskEndScheduler 00000018  00000000  00000000  0000101c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 83 .text.vTaskSuspendAll 00000026  00000000  00000000  00001034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 84 .text.uxTaskGetNumberOfTasks 0000000d  00000000  00000000  0000105c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 85 .text.xTaskGetIdleTaskHandle 0000003b  00000000  00000000  0000106c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 86 .text.xTaskGetIdleTaskHandleForCPU 0000002e  00000000  00000000  000010a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 87 .text.vTaskSwitchContext 0000028a  00000000  00000000  000010d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 88 .text.vTaskSetTimeOutState 00000034  00000000  00000000  00001364  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 89 .text.vTaskMissedYield 00000018  00000000  00000000  00001398  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 90 .text.vTaskAllocateMPURegions 00000028  00000000  00000000  000013b0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 91 .text.xTaskGetCurrentTaskHandle 0000001f  00000000  00000000  000013d8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 92 .text.__getreent 00000019  00000000  00000000  000013f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 93 .text.pcTaskGetTaskName 0000002d  00000000  00000000  00001414  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 94 .text.pvTaskGetThreadLocalStoragePointer 0000001e  00000000  00000000  00001444  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 95 .text.xTaskGetAffinity 00000013  00000000  00000000  00001464  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 96 .text.uxTaskGetStackHighWaterMark 0000001a  00000000  00000000  00001478  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 97 .text.pxTaskGetStackStart 00000012  00000000  00000000  00001494  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 98 .text.xTaskGetCurrentTaskHandleForCPU 00000018  00000000  00000000  000014a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 99 .text.xTaskGetSchedulerState 00000037  00000000  00000000  000014c0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+100 .text.vTaskEnterCritical 000000b3  00000000  00000000  000014f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+101 .text.vTaskExitCritical 0000008a  00000000  00000000  000015ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+102 .text.prvAddNewTaskToReadyList 00000173  00000000  00000000  00001638  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+103 .text.xTaskCreateRestricted 00000074  00000000  00000000  000017ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+104 .text.xTaskCreatePinnedToCore 00000076  00000000  00000000  00001820  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+105 .text.vTaskStartScheduler 00000072  00000000  00000000  00001898  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+106 .text.vTaskDelete 00000122  00000000  00000000  0000190c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+107 .text.vTaskDelayUntil 000000e7  00000000  00000000  00001a30  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+108 .text.vTaskDelay 00000080  00000000  00000000  00001b18  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+109 .text.eTaskGetState 000000aa  00000000  00000000  00001b98  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+110 .text.uxTaskPriorityGet 00000023  00000000  00000000  00001c44  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+111 .text.uxTaskPriorityGetFromISR 00000023  00000000  00000000  00001c68  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+112 .text.vTaskPrioritySet 00000143  00000000  00000000  00001c8c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+113 .text.vTaskSuspend 00000106  00000000  00000000  00001dd0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+114 .text.vTaskResume 000000d8  00000000  00000000  00001ed8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+115 .text.xTaskResumeFromISR 000000f6  00000000  00000000  00001fb0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+116 .text.prvCheckTasksWaitingTermination 000000c6  00000000  00000000  000020a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+117 .text.prvIdleTask 00000012  00000000  00000000  00002170  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+118 .text.xTaskGetTickCount 00000020  00000000  00000000  00002184  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+119 .text.xTaskGetTickCountFromISR 00000020  00000000  00000000  000021a4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+120 .text.xTaskIncrementTick 000001ce  00000000  00000000  000021c4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+121 .text.xTaskResumeAll 00000186  00000000  00000000  00002394  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+122 .text.vTaskPlaceOnEventList 00000097  00000000  00000000  0000251c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+123 .text.vTaskPlaceOnUnorderedEventList 000000cf  00000000  00000000  000025b4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+124 .text.vTaskPlaceOnEventListRestricted 0000006f  00000000  00000000  00002684  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+125 .text.xTaskRemoveFromEventList 00000143  00000000  00000000  000026f4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+126 .text.xTaskRemoveFromUnorderedEventList 000000ff  00000000  00000000  00002838  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+127 .text.xTaskCheckForTimeOut 00000096  00000000  00000000  00002938  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+128 .text.vTaskSetThreadLocalStoragePointerAndDelCallback 0000002f  00000000  00000000  000029d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+129 .text.vTaskSetThreadLocalStoragePointer 00000013  00000000  00000000  00002a00  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+130 .text.vTaskPriorityInherit 000000db  00000000  00000000  00002a14  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+131 .text.xTaskPriorityDisinherit 000000aa  00000000  00000000  00002af0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+132 .text.uxTaskResetEventItemValue 0000003e  00000000  00000000  00002b9c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+133 .text.pvTaskIncrementMutexHeldCount 00000054  00000000  00000000  00002bdc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+134 .text.ulTaskNotifyTake 0000012e  00000000  00000000  00002c30  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+135 .text.xTaskNotifyWait 00000156  00000000  00000000  00002d60  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+136 .text.xTaskNotify 0000014a  00000000  00000000  00002eb8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+137 .text.xTaskNotifyFromISR 00000172  00000000  00000000  00003004  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+138 .text.vTaskNotifyGiveFromISR 0000011c  00000000  00000000  00003178  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+139 .text.uxTaskGetSnapshotAll 0000009c  00000000  00000000  00003294  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+140 .rodata.__FUNCTION__$5930 00000017  00000000  00000000  00003330  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+141 .rodata.__FUNCTION__$5917 00000013  00000000  00000000  00003348  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+142 .rodata.__FUNCTION__$5901 0000000c  00000000  00000000  0000335c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+143 .rodata.__func__$5851 00000029  00000000  00000000  00003368  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+144 .rodata.__func__$5846 00000029  00000000  00000000  00003394  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+145 .rodata.__FUNCTION__$5808 00000018  00000000  00000000  000033c0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+146 .rodata.__FUNCTION__$5718 00000018  00000000  00000000  000033d8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+147 .rodata.__FUNCTION__$5683 00000015  00000000  00000000  000033f0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+148 .rodata.__FUNCTION__$5677 00000015  00000000  00000000  00003408  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+149 .rodata.__FUNCTION__$5673 00000022  00000000  00000000  00003420  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+150 .rodata.__FUNCTION__$5663 00000019  00000000  00000000  00003444  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+151 .rodata.__FUNCTION__$5654 00000020  00000000  00000000  00003460  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+152 .rodata.__FUNCTION__$5648 0000001f  00000000  00000000  00003480  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+153 .rodata.__FUNCTION__$5641 00000016  00000000  00000000  000034a0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+154 .rodata.ucExpectedStackBytes$5613 00000014  00000000  00000000  000034b8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+155 .rodata.__FUNCTION__$5605 00000013  00000000  00000000  000034cc  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+156 .rodata.__FUNCTION__$5596 0000001d  00000000  00000000  000034e0  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+157 .rodata.__FUNCTION__$5591 00000017  00000000  00000000  00003500  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+158 .rodata.__FUNCTION__$5587 00000012  00000000  00000000  00003518  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+159 .rodata.__FUNCTION__$5565 0000000f  00000000  00000000  0000352c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+160 .rodata.__FUNCTION__$5547 00000014  00000000  00000000  0000353c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+161 .rodata.__FUNCTION__$5536 00000013  00000000  00000000  00003550  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+162 .rodata.__FUNCTION__$5525 00000017  00000000  00000000  00003564  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+163 .rodata.__FUNCTION__$5530 0000000c  00000000  00000000  0000357c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+164 .rodata.__FUNCTION__$5519 0000000d  00000000  00000000  00003588  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+165 .rodata.__FUNCTION__$5513 00000011  00000000  00000000  00003598  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+166 .rodata.__FUNCTION__$5494 0000000e  00000000  00000000  000035ac  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+167 .rodata.__FUNCTION__$5485 0000000b  00000000  00000000  000035bc  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+168 .rodata.__FUNCTION__$5478 00000010  00000000  00000000  000035c8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+169 .rodata.__FUNCTION__$5772 0000000d  00000000  00000000  000035d8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+170 .rodata.__FUNCTION__$5776 0000000d  00000000  00000000  000035e8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+171 .rodata.__FUNCTION__$5470 0000000c  00000000  00000000  000035f8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+172 .rodata.__FUNCTION__$5463 00000019  00000000  00000000  00003604  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+173 .rodata.__FUNCTION__$5421 00000016  00000000  00000000  00003620  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+174 .bss.xSwitchingContext 00000008  00000000  00000000  00003638  2**2
+                  ALLOC
+175 .data.xTickCountMutex 00000008  00000000  00000000  00003638  2**2
+                  CONTENTS, ALLOC, LOAD, DATA
+176 .data.xTaskQueueMutex 00000008  00000000  00000000  00003640  2**2
+                  CONTENTS, ALLOC, LOAD, DATA
+177 .bss.uxSchedulerSuspended 00000008  00000000  00000000  00003648  2**2
+                  ALLOC
+178 .data.xNextTaskUnblockTime 00000004  00000000  00000000  00003648  2**2
+                  CONTENTS, ALLOC, LOAD, DATA
+179 .bss.uxTaskNumber 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+180 .bss.xNumOfOverflows 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+181 .bss.xYieldPending 00000008  00000000  00000000  0000364c  2**2
+                  ALLOC
+182 .bss.uxPendedTicks 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+183 .bss.xSchedulerRunning 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+184 .bss.uxTopReadyPriority 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+185 .bss.xTickCount 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+186 .bss.uxCurrentNumberOfTasks 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+187 .bss.xIdleTaskHandle 00000008  00000000  00000000  0000364c  2**2
+                  ALLOC
+188 .bss.xSuspendedTaskList 00000014  00000000  00000000  0000364c  2**2
+                  ALLOC
+189 .bss.uxTasksDeleted 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+190 .bss.xTasksWaitingTermination 00000014  00000000  00000000  0000364c  2**2
+                  ALLOC
+191 .bss.xPendingReadyList 00000028  00000000  00000000  0000364c  2**2
+                  ALLOC
+192 .bss.pxOverflowDelayedTaskList 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+193 .bss.pxDelayedTaskList 00000004  00000000  00000000  0000364c  2**2
+                  ALLOC
+194 .bss.xDelayedTaskList2 00000014  00000000  00000000  0000364c  2**2
+                  ALLOC
+195 .bss.xDelayedTaskList1 00000014  00000000  00000000  0000364c  2**2
+                  ALLOC
+196 .bss.pxReadyTasksLists 000001f4  00000000  00000000  0000364c  2**2
+                  ALLOC
+197 .bss.pxCurrentTCB 00000008  00000000  00000000  0000364c  2**2
+                  ALLOC
+198 .debug_frame  00000688  00000000  00000000  0000364c  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+199 .debug_info   00005e20  00000000  00000000  00003cd4  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+200 .debug_abbrev 0000046f  00000000  00000000  00009af4  2**0
+                  CONTENTS, READONLY, DEBUGGING
+201 .debug_loc    000022ef  00000000  00000000  00009f63  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+202 .debug_aranges 00000240  00000000  00000000  0000c252  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+203 .debug_ranges 000002c8  00000000  00000000  0000c492  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+204 .debug_line   0000291c  00000000  00000000  0000c75a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+205 .debug_str    0000190d  00000000  00000000  0000f076  2**0
+                  CONTENTS, READONLY, DEBUGGING
+206 .comment      0000003b  00000000  00000000  00010983  2**0
+                  CONTENTS, READONLY
+207 .xtensa.info  00000038  00000000  00000000  000109be  2**0
+                  CONTENTS, READONLY
+208 .xt.lit       00000218  00000000  00000000  000109f6  2**0
+                  CONTENTS, RELOC, READONLY
+209 .xt.prop      00002154  00000000  00000000  00010c0e  2**0
+                  CONTENTS, RELOC, READONLY
+
+timers.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.prvGetNextExpireTime 00000004  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.prvInsertTimerInActiveList 00000010  00000000  00000000  00000038  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.prvCheckForValidListAndQueue 00000044  00000000  00000000  00000048  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .literal.prvInitialiseNewTimer 0000001c  00000000  00000000  0000008c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .literal.xTimerCreateTimerTask 0000002c  00000000  00000000  000000a8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .literal.xTimerCreate 00000008  00000000  00000000  000000d4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .literal.xTimerGenericCommand 00000014  00000000  00000000  000000dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .literal.prvSwitchTimerLists 00000028  00000000  00000000  000000f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .literal.prvSampleTimeNow 0000000c  00000000  00000000  00000118  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .literal.prvProcessExpiredTimer 00000024  00000000  00000000  00000124  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .literal.prvProcessTimerOrBlockTask 00000024  00000000  00000000  00000148  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 11 .literal.prvProcessReceivedCommands 00000040  00000000  00000000  0000016c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 12 .literal.prvTimerTask 0000000c  00000000  00000000  000001ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 13 .literal.xTimerGetPeriod 00000014  00000000  00000000  000001b8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .literal.xTimerGetExpiryTime 00000014  00000000  00000000  000001cc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 15 .literal.xTimerIsTimerActive 0000000c  00000000  00000000  000001e0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .literal.vTimerSetTimerID 00000014  00000000  00000000  000001ec  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .literal.xTimerPendFunctionCallFromISR 00000008  00000000  00000000  00000200  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .literal.xTimerPendFunctionCall 0000001c  00000000  00000000  00000208  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .text         00000000  00000000  00000000  00000224  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 20 .data         00000000  00000000  00000000  00000224  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+ 21 .bss          00000000  00000000  00000000  00000224  2**0
+                  ALLOC
+ 22 .text.prvGetNextExpireTime 00000020  00000000  00000000  00000224  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .text.prvInsertTimerInActiveList 00000054  00000000  00000000  00000244  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .rodata.str1.4 00000058  00000000  00000000  00000298  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 25 .text.prvCheckForValidListAndQueue 0000007e  00000000  00000000  000002f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 26 .text.prvInitialiseNewTimer 0000003a  00000000  00000000  00000370  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 27 .text.xTimerCreateTimerTask 0000004c  00000000  00000000  000003ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 28 .text.xTimerCreate 00000026  00000000  00000000  000003f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 29 .text.xTimerGenericCommand 0000005d  00000000  00000000  00000420  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 30 .text.prvSwitchTimerLists 00000082  00000000  00000000  00000480  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 31 .text.prvSampleTimeNow 0000002f  00000000  00000000  00000504  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 32 .text.prvProcessExpiredTimer 0000005f  00000000  00000000  00000534  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 33 .text.prvProcessTimerOrBlockTask 0000006c  00000000  00000000  00000594  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 34 .text.prvProcessReceivedCommands 000000e6  00000000  00000000  00000600  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 35 .rodata.prvProcessReceivedCommands 00000028  00000000  00000000  000006e8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
+ 36 .text.prvTimerTask 0000001d  00000000  00000000  00000710  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 37 .text.xTimerGetPeriod 00000022  00000000  00000000  00000730  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 38 .text.xTimerGetExpiryTime 00000022  00000000  00000000  00000754  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 39 .text.pcTimerGetTimerName 00000007  00000000  00000000  00000778  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 40 .text.xTimerIsTimerActive 00000024  00000000  00000000  00000780  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 41 .text.pvTimerGetTimerID 00000007  00000000  00000000  000007a4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 42 .text.vTimerSetTimerID 00000022  00000000  00000000  000007ac  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 43 .text.xTimerPendFunctionCallFromISR 00000022  00000000  00000000  000007d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 44 .text.xTimerPendFunctionCall 0000003c  00000000  00000000  000007f4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 45 .rodata.__FUNCTION__$5352 00000017  00000000  00000000  00000830  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 46 .rodata.__FUNCTION__$5335 00000011  00000000  00000000  00000848  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 47 .rodata.__FUNCTION__$5240 00000014  00000000  00000000  0000085c  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 48 .rodata.__FUNCTION__$5234 00000010  00000000  00000000  00000870  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 49 .rodata.__FUNCTION__$5220 00000016  00000000  00000000  00000880  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 50 .rodata.__FUNCTION__$5320 0000001d  00000000  00000000  00000898  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 51 .rodata.__FUNCTION__$5289 0000001b  00000000  00000000  000008b8  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 52 .rodata.__FUNCTION__$5251 00000017  00000000  00000000  000008d4  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 53 .rodata.__FUNCTION__$5313 00000014  00000000  00000000  000008ec  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 54 .bss.xLastTime$5272 00000004  00000000  00000000  00000900  2**2
+                  ALLOC
+ 55 .rodata.__FUNCTION__$5203 00000016  00000000  00000000  00000900  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 56 .data.xTimerMux 00000008  00000000  00000000  00000918  2**2
+                  CONTENTS, ALLOC, LOAD, DATA
+ 57 .bss.xTimerQueue 00000004  00000000  00000000  00000920  2**2
+                  ALLOC
+ 58 .bss.pxOverflowTimerList 00000004  00000000  00000000  00000920  2**2
+                  ALLOC
+ 59 .bss.pxCurrentTimerList 00000004  00000000  00000000  00000920  2**2
+                  ALLOC
+ 60 .bss.xActiveTimerList2 00000014  00000000  00000000  00000920  2**2
+                  ALLOC
+ 61 .bss.xActiveTimerList1 00000014  00000000  00000000  00000920  2**2
+                  ALLOC
+ 62 .debug_frame  00000208  00000000  00000000  00000920  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 63 .debug_info   00001405  00000000  00000000  00000b28  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 64 .debug_abbrev 00000279  00000000  00000000  00001f2d  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 65 .debug_loc    0000062f  00000000  00000000  000021a6  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 66 .debug_aranges 000000c0  00000000  00000000  000027d5  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 67 .debug_ranges 000000b0  00000000  00000000  00002895  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 68 .debug_line   00000771  00000000  00000000  00002945  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 69 .debug_str    00000934  00000000  00000000  000030b6  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 70 .comment      0000003b  00000000  00000000  000039ea  2**0
+                  CONTENTS, READONLY
+ 71 .xtensa.info  00000038  00000000  00000000  00003a25  2**0
+                  CONTENTS, READONLY
+ 72 .xt.lit       00000098  00000000  00000000  00003a5d  2**0
+                  CONTENTS, RELOC, READONLY
+ 73 .xt.prop      00000714  00000000  00000000  00003af5  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_context.S.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal      00000024  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .text         0000013a  00000000  00000000  00000058  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .data         00000000  00000000  00000000  00000192  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  3 .bss          00000000  00000000  00000000  00000192  2**0
+                  ALLOC
+  4 .xtensa.info  00000038  00000000  00000000  00000192  2**0
+                  CONTENTS, READONLY
+  5 .debug_line   00000313  00000000  00000000  000001ca  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  6 .debug_info   0000009a  00000000  00000000  000004dd  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  7 .debug_abbrev 00000014  00000000  00000000  00000577  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  8 .debug_aranges 00000020  00000000  00000000  00000590  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  9 .xt.lit       00000008  00000000  00000000  000005b0  2**0
+                  CONTENTS, RELOC, READONLY
+ 10 .xt.prop      000000e4  00000000  00000000  000005b8  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_init.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal._xt_tick_divisor_init 0000000c  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.xt_clock_freq 00000004  00000000  00000000  00000040  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .text         00000000  00000000  00000000  00000044  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  3 .data         00000000  00000000  00000000  00000044  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  4 .bss          00000000  00000000  00000000  00000044  2**0
+                  ALLOC
+  5 .text._xt_tick_divisor_init 0000001f  00000000  00000000  00000044  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .text.xt_clock_freq 0000000d  00000000  00000000  00000064  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .bss._xt_tick_divisor 00000004  00000000  00000000  00000074  2**2
+                  ALLOC
+  8 .debug_frame  00000040  00000000  00000000  00000074  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  9 .debug_info   000000d4  00000000  00000000  000000b4  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 10 .debug_abbrev 0000008b  00000000  00000000  00000188  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 11 .debug_aranges 00000028  00000000  00000000  00000213  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 12 .debug_ranges 00000018  00000000  00000000  0000023b  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 13 .debug_line   000000c8  00000000  00000000  00000253  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 14 .debug_str    00000180  00000000  00000000  0000031b  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 15 .comment      0000003b  00000000  00000000  0000049b  2**0
+                  CONTENTS, READONLY
+ 16 .xtensa.info  00000038  00000000  00000000  000004d6  2**0
+                  CONTENTS, READONLY
+ 17 .xt.lit       00000010  00000000  00000000  0000050e  2**0
+                  CONTENTS, RELOC, READONLY
+ 18 .xt.prop      0000006c  00000000  00000000  0000051e  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_intr.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.xt_unhandled_interrupt 00000008  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.xt_set_exception_handler 00000008  00000000  00000000  0000003c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.xt_set_interrupt_handler 0000000c  00000000  00000000  00000044  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .text         00000000  00000000  00000000  00000050  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  4 .data         00000000  00000000  00000000  00000050  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  5 .bss          00000000  00000000  00000000  00000050  2**0
+                  ALLOC
+  6 .rodata.str1.4 00000023  00000000  00000000  00000050  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+  7 .text.xt_unhandled_interrupt 00000016  00000000  00000000  00000074  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .text.xt_set_exception_handler 00000042  00000000  00000000  0000008c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .text.xt_set_interrupt_handler 00000046  00000000  00000000  000000d0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .debug_frame  00000058  00000000  00000000  00000118  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 11 .debug_info   0000042d  00000000  00000000  00000170  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 12 .debug_abbrev 000001b6  00000000  00000000  0000059d  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 13 .debug_loc    0000015c  00000000  00000000  00000753  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 14 .debug_aranges 00000030  00000000  00000000  000008af  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 15 .debug_ranges 00000020  00000000  00000000  000008df  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 16 .debug_line   000002b8  00000000  00000000  000008ff  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 17 .debug_str    000002a5  00000000  00000000  00000bb7  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 18 .comment      0000003b  00000000  00000000  00000e5c  2**0
+                  CONTENTS, READONLY
+ 19 .xtensa.info  00000038  00000000  00000000  00000e97  2**0
+                  CONTENTS, READONLY
+ 20 .xt.lit       00000018  00000000  00000000  00000ecf  2**0
+                  CONTENTS, RELOC, READONLY
+ 21 .xt.prop      00000120  00000000  00000000  00000ee7  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_intr_asm.S.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .text         00000033  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  1 .data         00000400  00000000  00000000  00000068  2**3
+                  CONTENTS, ALLOC, LOAD, RELOC, DATA
+  2 .bss          00000000  00000000  00000000  00000468  2**0
+                  ALLOC
+  3 .xtensa.info  00000038  00000000  00000000  00000468  2**0
+                  CONTENTS, READONLY
+  4 .debug_line   000000da  00000000  00000000  000004a0  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  5 .debug_info   0000009b  00000000  00000000  0000057a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  6 .debug_abbrev 00000014  00000000  00000000  00000615  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  7 .debug_aranges 00000020  00000000  00000000  00000630  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  8 .xt.prop      00000054  00000000  00000000  00000650  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_overlay_os_hook.c.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .literal.xt_overlay_init_os 00000008  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .literal.xt_overlay_lock 00000008  00000000  00000000  0000003c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .literal.xt_overlay_unlock 00000008  00000000  00000000  00000044  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .text         00000000  00000000  00000000  0000004c  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  4 .data         00000000  00000000  00000000  0000004c  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  5 .bss          00000000  00000000  00000000  0000004c  2**0
+                  ALLOC
+  6 .text.xt_overlay_init_os 00000013  00000000  00000000  0000004c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .text.xt_overlay_lock 00000016  00000000  00000000  00000060  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .text.xt_overlay_unlock 00000016  00000000  00000000  00000078  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .bss.xt_overlay_mutex 00000004  00000000  00000000  00000090  2**2
+                  ALLOC
+ 10 .debug_frame  00000058  00000000  00000000  00000090  2**2
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 11 .debug_info   00000193  00000000  00000000  000000e8  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 12 .debug_abbrev 0000008f  00000000  00000000  0000027b  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 13 .debug_aranges 00000030  00000000  00000000  0000030a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 14 .debug_ranges 00000020  00000000  00000000  0000033a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 15 .debug_line   00000192  00000000  00000000  0000035a  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 16 .debug_str    0000025c  00000000  00000000  000004ec  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 17 .comment      0000003b  00000000  00000000  00000748  2**0
+                  CONTENTS, READONLY
+ 18 .xtensa.info  00000038  00000000  00000000  00000783  2**0
+                  CONTENTS, READONLY
+ 19 .xt.lit       00000018  00000000  00000000  000007bb  2**0
+                  CONTENTS, RELOC, READONLY
+ 20 .xt.prop      0000009c  00000000  00000000  000007d3  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_vector_defaults.S.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .iram1.literal 00000004  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .text         00000000  00000000  00000000  00000038  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+  2 .data         00000000  00000000  00000000  00000038  2**0
+                  CONTENTS, ALLOC, LOAD, DATA
+  3 .bss          00000000  00000000  00000000  00000038  2**0
+                  ALLOC
+  4 .iram1        00000032  00000000  00000000  00000038  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .xtensa.info  00000038  00000000  00000000  0000006a  2**0
+                  CONTENTS, READONLY
+  6 .debug_line   000000c2  00000000  00000000  000000a2  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  7 .debug_info   000000a2  00000000  00000000  00000164  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+  8 .debug_abbrev 00000014  00000000  00000000  00000206  2**0
+                  CONTENTS, READONLY, DEBUGGING
+  9 .debug_aranges 00000020  00000000  00000000  00000220  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 10 .xt.lit       00000008  00000000  00000000  00000240  2**0
+                  CONTENTS, RELOC, READONLY
+ 11 .xt.prop      00000078  00000000  00000000  00000248  2**0
+                  CONTENTS, RELOC, READONLY
+
+xtensa_vectors.S.obj:     file format elf32-xtensa-le
+
+Sections:
+Idx Name          Size      VMA       LMA       File off  Algn
+  0 .iram1.literal 000000a8  00000000  00000000  00000034  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  1 .DebugExceptionVector.literal 00000004  00000000  00000000  000000dc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  2 .DoubleExceptionVector.literal 00000004  00000000  00000000  000000e0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  3 .KernelExceptionVector.literal 00000004  00000000  00000000  000000e4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  4 .UserExceptionVector.literal 00000004  00000000  00000000  000000e8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  5 .Level2InterruptVector.literal 00000004  00000000  00000000  000000ec  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  6 .Level3InterruptVector.literal 00000004  00000000  00000000  000000f0  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  7 .Level4InterruptVector.literal 00000004  00000000  00000000  000000f4  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  8 .Level5InterruptVector.literal 00000004  00000000  00000000  000000f8  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+  9 .NMIExceptionVector.literal 00000004  00000000  00000000  000000fc  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 10 .text         00000000  00000000  00000000  00000100  2**0
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 11 .data         00000008  00000000  00000000  00000100  2**4
+                  CONTENTS, ALLOC, LOAD, DATA
+ 12 .bss          00000000  00000000  00000000  00000108  2**0
+                  ALLOC
+ 13 .iram1        000004ec  00000000  00000000  00000108  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 14 .rodata       00000024  00000000  00000000  00000600  2**4
+                  CONTENTS, ALLOC, LOAD, READONLY, DATA
+ 15 .DebugExceptionVector.text 00000009  00000000  00000000  00000624  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 16 .DoubleExceptionVector.text 00000011  00000000  00000000  00000630  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 17 .KernelExceptionVector.text 00000009  00000000  00000000  00000644  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 18 .UserExceptionVector.text 00000009  00000000  00000000  00000650  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 19 .Level2InterruptVector.text 00000009  00000000  00000000  0000065c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 20 .Level3InterruptVector.text 00000009  00000000  00000000  00000668  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 21 .Level4InterruptVector.text 00000009  00000000  00000000  00000674  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 22 .Level5InterruptVector.text 00000009  00000000  00000000  00000680  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 23 .NMIExceptionVector.text 00000009  00000000  00000000  0000068c  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 24 .WindowVectors.text 0000016a  00000000  00000000  00000698  2**2
+                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
+ 25 .UserEnter.text 00000000  00000000  00000000  00000804  2**2
+                  CONTENTS, ALLOC, LOAD, READONLY, CODE
+ 26 .xtensa.info  00000038  00000000  00000000  00000804  2**0
+                  CONTENTS, READONLY
+ 27 .debug_line   000009b8  00000000  00000000  0000083c  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 28 .debug_info   00000096  00000000  00000000  000011f4  2**0
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 29 .debug_abbrev 00000012  00000000  00000000  0000128a  2**0
+                  CONTENTS, READONLY, DEBUGGING
+ 30 .debug_aranges 00000070  00000000  00000000  000012a0  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 31 .debug_ranges 00000068  00000000  00000000  00001310  2**3
+                  CONTENTS, RELOC, READONLY, DEBUGGING
+ 32 .xt.lit       00000050  00000000  00000000  00001378  2**0
+                  CONTENTS, RELOC, READONLY
+ 33 .xt.prop      000004f8  00000000  00000000  000013c8  2**0
+                  CONTENTS, RELOC, READONLY
diff --git a/tools/ldgen/test/data/template.ld b/tools/ldgen/test/data/template.ld
new file mode 100644 (file)
index 0000000..02a0d21
--- /dev/null
@@ -0,0 +1,217 @@
+/*  Default entry point:  */
+ENTRY(call_start_cpu0);
+
+SECTIONS
+{
+  /* RTC fast memory holds RTC wake stub code,
+     including from any source file named rtc_wake_stub*.c
+  */
+  .rtc.text :
+  {
+    . = ALIGN(4);
+
+    mapping[rtc_text]
+
+    *rtc_wake_stub*.o(.literal .text .literal.* .text.*)
+  } >rtc_iram_seg
+
+  /* RTC slow memory holds RTC wake stub
+     data/rodata, including from any source file
+     named rtc_wake_stub*.c
+  */
+  .rtc.data :
+  {
+    _rtc_data_start = ABSOLUTE(.);
+
+    mapping[rtc_data]
+
+    *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*)
+    _rtc_data_end = ABSOLUTE(.);
+  } > rtc_slow_seg
+
+  /* RTC bss, from any source file named rtc_wake_stub*.c */
+  .rtc.bss (NOLOAD) :
+  {
+    _rtc_bss_start = ABSOLUTE(.);
+
+    mapping[rtc_bss]
+
+    *rtc_wake_stub*.o(.bss .bss.*)
+    *rtc_wake_stub*.o(COMMON)
+    _rtc_bss_end = ABSOLUTE(.);
+  } > rtc_slow_seg
+
+  /* Send .iram0 code to iram */
+  .iram0.vectors :
+  {
+    /* Vectors go to IRAM */
+    _init_start = ABSOLUTE(.);
+    /* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */
+    . = 0x0;
+    KEEP(*(.WindowVectors.text));
+    . = 0x180;
+    KEEP(*(.Level2InterruptVector.text));
+    . = 0x1c0;
+    KEEP(*(.Level3InterruptVector.text));
+    . = 0x200;
+    KEEP(*(.Level4InterruptVector.text));
+    . = 0x240;
+    KEEP(*(.Level5InterruptVector.text));
+    . = 0x280;
+    KEEP(*(.DebugExceptionVector.text));
+    . = 0x2c0;
+    KEEP(*(.NMIExceptionVector.text));
+    . = 0x300;
+    KEEP(*(.KernelExceptionVector.text));
+    . = 0x340;
+    KEEP(*(.UserExceptionVector.text));
+    . = 0x3C0;
+    KEEP(*(.DoubleExceptionVector.text));
+    . = 0x400;
+    *(.*Vector.literal)
+
+    *(.UserEnter.literal);
+    *(.UserEnter.text);
+    . = ALIGN (16);
+    *(.entry.text)
+    *(.init.literal)
+    *(.init)
+    _init_end = ABSOLUTE(.);
+
+    /* This goes here, not at top of linker script, so addr2line finds it last,
+       and uses it in preference to the first symbol in IRAM */
+    _iram_start = ABSOLUTE(0);
+  } > iram0_0_seg
+
+  .iram0.text :
+  {
+    /* Code marked as runnning out of IRAM */
+    _iram_text_start = ABSOLUTE(.);
+
+    mapping[iram0_text]
+    
+    INCLUDE esp32.spiram.rom-functions-iram.ld
+    _iram_text_end = ABSOLUTE(.);
+  } > iram0_0_seg
+
+  .dram0.data :
+  {
+    _data_start = ABSOLUTE(.);
+
+    mapping[dram0_data]
+
+    *(.gnu.linkonce.d.*)
+    *(.data1)
+    *(.sdata)
+    *(.sdata.*)
+    *(.gnu.linkonce.s.*)
+    *(.sdata2)
+    *(.sdata2.*)
+    *(.gnu.linkonce.s2.*)
+    *(.jcr)
+    INCLUDE esp32.spiram.rom-functions-dram.ld
+    _data_end = ABSOLUTE(.);
+    . = ALIGN(4);
+  } >dram0_0_seg
+
+  /* Shared RAM */
+  .dram0.bss (NOLOAD) :
+  {
+    . = ALIGN (8);
+    _bss_start = ABSOLUTE(.);
+
+    mapping[dram0_bss]
+
+    *(.dynsbss)
+    *(.sbss)
+    *(.sbss.*)
+    *(.gnu.linkonce.sb.*)
+    *(.scommon)
+    *(.sbss2)
+    *(.sbss2.*)
+    *(.gnu.linkonce.sb2.*)
+    *(.dynbss)
+    *(.share.mem)
+    *(.gnu.linkonce.b.*)
+
+    . = ALIGN (8);
+    _bss_end = ABSOLUTE(.);
+    _heap_start = ABSOLUTE(.);
+  } >dram0_0_seg
+
+  .flash.rodata :
+  {
+    _rodata_start = ABSOLUTE(.);
+
+    mapping[flash_rodata]
+
+    *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
+    *(.gnu.linkonce.r.*)
+    *(.rodata1)
+    __XT_EXCEPTION_TABLE_ = ABSOLUTE(.);
+    *(.xt_except_table)
+    *(.gcc_except_table .gcc_except_table.*)
+    *(.gnu.linkonce.e.*)
+    *(.gnu.version_r)
+    . = (. + 3) & ~ 3;
+    __eh_frame = ABSOLUTE(.);
+    KEEP(*(.eh_frame))
+    . = (. + 7) & ~ 3;
+    /*  C++ constructor and destructor tables, properly ordered:  */
+    __init_array_start = ABSOLUTE(.);
+    KEEP (*crtbegin.o(.ctors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+    __init_array_end = ABSOLUTE(.);
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+    /*  C++ exception handlers table:  */
+    __XT_EXCEPTION_DESCS_ = ABSOLUTE(.);
+    *(.xt_except_desc)
+    *(.gnu.linkonce.h.*)
+    __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
+    *(.xt_except_desc_end)
+    *(.dynamic)
+    *(.gnu.version_d)
+    _rodata_end = ABSOLUTE(.);
+    /* Literals are also RO data. */
+    _lit4_start = ABSOLUTE(.);
+    *(*.lit4)
+    *(.lit4.*)
+    *(.gnu.linkonce.lit4.*)
+    _lit4_end = ABSOLUTE(.);
+    . = ALIGN(4);
+    _thread_local_start = ABSOLUTE(.);
+    *(.tdata)
+    *(.tdata.*)
+    *(.tbss)
+    *(.tbss.*)
+    _thread_local_end = ABSOLUTE(.);
+    . = ALIGN(4);
+  } >drom0_0_seg
+
+  .flash.text :
+  {
+    _stext = .;
+    _text_start = ABSOLUTE(.);
+
+    mapping[flash_text]
+
+    *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
+    *(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */
+    *(.fini.literal)
+    *(.fini)
+    *(.gnu.version)
+    _text_end = ABSOLUTE(.);
+    _etext = .;
+
+    /* Similar to _iram_start, this symbol goes here so it is
+       resolved by addr2line in preference to the first symbol in
+       the flash.text segment.
+    */
+    _flash_cache_start = ABSOLUTE(0);
+  } >iram0_2_seg
+}
diff --git a/tools/ldgen/test/test_fragments.py b/tools/ldgen/test/test_fragments.py
new file mode 100755 (executable)
index 0000000..084079d
--- /dev/null
@@ -0,0 +1,632 @@
+#!/usr/bin/env python
+#
+# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import unittest
+import sys
+import os
+
+sys.path.append('../')
+from fragments import *
+from pyparsing import *
+from sdkconfig import *
+
+class FragmentTest(unittest.TestCase):
+
+    def parse(self, text):
+        self.parser.ignore("#" + restOfLine)
+        fragment = self.parser.parseString(text, parseAll=True)
+        return fragment[0]
+
+class SectionsTest(FragmentTest):
+
+    def setUp(self):
+        self.parser = Sections.get_fragment_grammar()
+
+    def test_valid_entries(self):
+        valid_entries = """
+        [sections:test]
+        entries:
+            .section1
+                .section2
+
+            # Section 3 should not exist
+            # section3
+            .section4
+
+            # This is a comment
+
+            .section5
+        """
+
+        sections = self.parse(valid_entries)
+
+        self.assertEqual("test", sections.name)
+
+        entries = sections.entries
+
+        expected = {
+            ".section1",
+            ".section2",
+            ".section4",
+            ".section5"
+        }
+
+        self.assertEqual(set(entries), expected)
+
+    def test_blank_entries(self):
+        blank_entries = """
+        [sections:test]
+        entries:
+        """
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(blank_entries)
+
+    def test_invalid_names(self):
+        with_spaces = """
+        [sections:invalid name 1]
+        entries:
+        """
+
+        begins_with_number = """
+        [sections:2invalid_name]
+        entries:
+        """
+
+        with_special_character = """
+        [sections:invalid_name~]
+        entries:
+        """
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(with_spaces)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(begins_with_number)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(with_special_character)
+
+    def test_non_existent_entries(self):
+        misspelled_entries_field = """
+        [sections:test]
+        entrie:
+            .section1
+        """
+
+        missing_entries_field = """
+        [sections:test]
+        """
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(misspelled_entries_field)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(missing_entries_field)
+
+    def test_duplicate_entries(self):
+        duplicate_entries = """
+        [sections:test]
+        entries:
+            .section1
+            .section3
+            .section1
+            .section1
+            .section2
+            .section3
+            .section1
+        """
+
+        sections = self.parse(duplicate_entries)
+
+        entries = sections.entries
+
+        expected = {
+            ".section1",
+            ".section2",
+            ".section3",
+        }
+
+        self.assertEqual(set(entries), expected)
+
+class SchemeTest(FragmentTest):
+
+    def setUp(self):
+        self.parser = Scheme.get_fragment_grammar()
+
+    def test_valid_entries(self):
+        valid_entries = """
+        [scheme:test]
+        entries:
+            sections1 -> target1
+            sections2   ->    target2
+        """
+
+        scheme = self.parse(valid_entries)
+
+        entries = scheme.entries
+
+        expected = {
+            ("sections1", "target1"),
+            ("sections2", "target2")
+        }
+
+        self.assertEqual(entries, expected)
+
+    def test_duplicate_same_mapping(self):
+        duplicate_entries = """
+        [scheme:duplicate_same_mapping]
+        entries:
+            sections1 -> target1
+            sections2 -> target2
+            sections1 -> target1
+        """
+
+        scheme = self.parse(duplicate_entries)
+
+        entries = scheme.entries
+
+        expected = {
+            ("sections1", "target1"),
+            ("sections2", "target2")
+        }
+
+        self.assertEqual(len(entries), 2)
+        self.assertEqual(entries, expected)
+
+    def test_invalid_separator(self):
+        wrong_character = """
+        [scheme:test]
+        entries:
+            sections1, target1
+        """
+
+        single_word = """
+        [scheme:test]
+        entries:
+            sections1
+        """
+
+        with self.assertRaises(ParseException):
+            scheme = self.parse(wrong_character)
+
+        with self.assertRaises(ParseException):
+            scheme = self.parse(single_word)
+
+    def test_blank_entries(self):
+        blank_entries = """
+        [scheme:test]
+        entries:
+        """
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(blank_entries)
+
+    def test_non_existent_entries(self):
+        misspelled_entries_field = """
+        [scheme:test]
+        entrie:
+            section -> target
+        """
+
+        missing_entries_field = """
+        [scheme:test]
+        """
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(misspelled_entries_field)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(missing_entries_field)
+
+class MappingTest(FragmentTest):
+
+    def setUp(self):
+        self.parser = Mapping.get_fragment_grammar()
+
+    def parse_expression(self, expression):
+        parser = SDKConfig.get_expression_grammar()
+        return parser.parseString(expression, parseAll=True)
+
+    def test_valid_grammar(self):
+        valid_entries = """
+        [mapping]
+        archive: lib.a
+        entries:
+            obj:symbol (noflash)
+            # Comments should not matter
+            obj (noflash)
+            # Nor should whitespace
+                            obj  :     symbol_2 (    noflash )
+              obj_2  (    noflash )
+            * (noflash)
+        """
+
+        mapping = self.parse(valid_entries)
+
+        self.assertEqual("lib.a", mapping.archive)
+        self.assertEqual("lib_a", mapping.name)
+
+        entries = mapping.entries
+
+        expected = [("default", {
+                ("obj", "symbol", "noflash"),
+                ("obj", None, "noflash"),
+                ("obj", "symbol_2", "noflash"),
+                ("obj_2", None, "noflash"),
+                ("*", None, "noflash")
+                } ) ]
+
+        self.assertEqual(entries, expected)
+
+    def test_invalid_grammar(self):
+        with_fragment_name = """
+        [mapping:name]
+        archive: lib.a
+        entries:
+            obj:symbol (noflash)
+        """
+
+        missing_archive = """
+        [mapping:name]
+        entries:
+            obj:symbol (noflash)
+        """
+
+        misspelled_archive = """
+        [mapping:name]
+        archi: lib.a
+        entries:
+            obj:symbol (noflash)
+        """
+
+        missing_entries = """
+        [mapping]
+        archive: lib.a
+        """
+
+        misspelled_entries = """
+        [mapping]
+        archive: lib.a
+        entrie:
+            obj:symbol (noflash)
+        """
+
+        missing_symbols = """
+        [mapping]
+        archive: lib.a
+        entries:
+            obj: (noflash)
+        """
+
+        missing_scheme_1 = """
+        [mapping]
+        archive: lib.a
+        entries:
+            obj: ()
+        """
+
+        missing_scheme_2 = """
+        [mapping]
+        archive: lib.a
+        entries:
+            obj:symbol
+        """
+
+        missing_entity = """
+        [mapping]
+        archive: lib.a
+        entries:
+            (noflash)
+        """
+
+        wilcard_symbol = """
+        [mapping]
+        archive: lib.a
+        entries:
+            obj:* (noflash)
+        """
+
+        empty_object_with_symbol = """
+        [mapping]
+        archive: lib.a
+        entries:
+            :symbol (noflash)
+        """
+
+        wildcard_object_with_symbol = """
+        [mapping]
+        archive: lib.a
+        entries:
+            *:symbol (noflash)
+        """
+
+        empty_definition = """
+        [mapping]
+        """
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(with_fragment_name)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(missing_archive)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(misspelled_archive)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(missing_entries)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(misspelled_entries)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(missing_symbols)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(missing_scheme_1)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(missing_scheme_2)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(missing_entity)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(wilcard_symbol)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(empty_object_with_symbol)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(wildcard_object_with_symbol)
+
+        with self.assertRaises(ParseException):
+            sections = self.parse(empty_definition)
+
+    def test_explicit_blank_default_w_others(self):
+        expl_blnk_w_oth = """
+        [mapping]
+        archive: lib.a
+        entries:
+            : CONFIG_A = y
+            obj_a (noflash)
+            : default
+        """
+
+        mapping = self.parse(expl_blnk_w_oth)
+
+        entries = mapping.entries
+
+        expected = [ ( entries[0][0] , {
+                ("obj_a", None, "noflash"),
+                } ),
+                ("default", set() ) ]
+
+        self.assertEqual(entries, expected)
+
+
+    def test_implicit_blank_default_w_others(self):
+        impl_blnk_w_oth = """
+        [mapping]
+        archive: lib.a
+        entries:
+            : CONFIG_A = y
+            obj_a (noflash)
+        """
+
+        mapping = self.parse(impl_blnk_w_oth)
+
+        entries = mapping.entries
+
+        expected = [ ( entries[0][0] , {
+                ("obj_a", None, "noflash"),
+                } ),
+                ("default", set() ) ]
+
+        self.assertEqual(entries, expected)
+
+    def test_explicit_blank_default(self):
+        expl_blnk_def = """
+        [mapping]
+        archive: lib.a
+        entries:
+            : default
+        """
+        mapping = self.parse(expl_blnk_def)
+        entries = mapping.entries
+        expected = [ ("default", set() ) ]
+
+        self.assertEqual(entries, expected)
+
+    def test_implicit_blank_default(self):
+        impl_blnk_def = """
+        [mapping]
+        archive: lib.a
+        entries:
+            : default
+        """
+        mapping = self.parse(impl_blnk_def)
+        entries = mapping.entries
+        expected = [ ("default", set() ) ]
+
+        self.assertEqual(entries, expected)
+
+    def test_multiple_entries(self):
+        multiple_entries = """
+        [mapping]
+        archive: lib.a
+        entries:
+            : CONFIG_A = y
+            obj_a1 (noflash)
+            obj_a2 (noflash)
+            : CONFIG_B = y
+            obj_b1 (noflash)
+            obj_b2 (noflash)
+            obj_b3 (noflash)
+            : CONFIG_C = y
+            obj_c1 (noflash)
+        """
+
+        mapping = self.parse(multiple_entries)
+
+        entries = mapping.entries
+
+        expected = [ ( entries[0][0] , {
+                ("obj_a1", None, "noflash"),
+                ("obj_a2", None, "noflash"),
+                } ),
+                ( entries[1][0] , {
+                ("obj_b1", None, "noflash"),
+                ("obj_b2", None, "noflash"),
+                ("obj_b3", None, "noflash"),
+                } ),
+                ( entries[2][0] , {
+                ("obj_c1", None, "noflash"),
+                } ),
+                ("default", set() ) ]
+
+        self.assertEqual(entries, expected)
+
+    def test_blank_entries(self):
+        blank_entries = """
+        [mapping]
+        archive: lib.a
+        entries:
+            : CONFIG_A = y
+            obj_a (noflash)
+            : CONFIG_B = y
+            : CONFIG_C = y
+            obj_c (noflash)
+            : CONFIG_D = y
+            : CONFIG_E = y
+            : default
+            obj (noflash)
+        """
+
+        mapping = self.parse(blank_entries)
+
+        entries = mapping.entries
+
+        expected = [ ( entries[0][0] , {
+                ("obj_a", None, "noflash")
+                } ),
+                ( entries[1][0] , set()),
+                ( entries[2][0] , {
+                ("obj_c", None, "noflash")
+                } ),
+                ( entries[3][0] , set()),
+                ( entries[4][0] , set()),
+                ( "default" , {
+                ("obj", None, "noflash")
+                } ) ]
+
+        self.assertEqual(entries, expected)
+
+    def test_blank_first_condition(self):
+        blank_first_condition = """
+        [mapping]
+        archive: lib.a
+        entries:
+            obj_a (noflash)
+            : CONFIG_B = y
+            obj_b (noflash)
+        """
+
+        with self.assertRaises(ParseException):
+            mapping = self.parse(blank_first_condition)
+
+
+    def test_nonlast_default(self):
+        nonlast_default_1 = """
+        [mapping]
+        archive: lib.a
+        entries:
+            : default
+            obj_a (noflash)
+            : CONFIG_A = y
+            obj_A (noflash)
+        """
+
+        nonlast_default_2 = """
+        [mapping]
+        archive: lib.a
+        entries:
+            : CONFIG_A = y
+            obj_A (noflash)
+            : default
+            obj_a (noflash)
+            : CONFIG_B = y
+            obj_B (noflash)
+        """
+
+        nonlast_default_3 = """
+        [mapping]
+        archive: lib.a
+        entries:
+            : CONFIG_A = y
+            obj_A (noflash)
+            :
+            obj_a (noflash)
+            : CONFIG_B = y
+            obj_B (noflash)
+        """
+
+        with self.assertRaises(ParseException):
+            mapping = self.parse(nonlast_default_1)
+
+        with self.assertRaises(ParseException):
+            mapping = self.parse(nonlast_default_2)
+
+        with self.assertRaises(ParseException):
+            mapping = self.parse(nonlast_default_3)
+
+    def test_duplicate_default(self):
+        duplicate_default_1 = """
+        archive: lib.a
+        entries:
+            : CONFIG_A = y
+            obj_A (noflash)
+            : default
+            obj_a (noflash)
+            : CONFIG_B = y
+            obj_B (noflash)
+            : default
+            obj_a (noflash)
+        """
+
+        duplicate_default_2 = """
+        archive: lib.a
+        entries:
+            : CONFIG_A = y
+            obj_A (noflash)
+            : CONFIG_B = y
+            obj_a (noflash)
+            : default
+            obj_B (noflash)
+            :
+            obj_a (noflash)
+        """
+
+        with self.assertRaises(ParseException):
+            mapping = self.parse(duplicate_default_1)
+
+        with self.assertRaises(ParseException):
+            mapping = self.parse(duplicate_default_2)
+
+if __name__ =="__main__":
+    unittest.main()
diff --git a/tools/ldgen/test/test_generation.py b/tools/ldgen/test/test_generation.py
new file mode 100755 (executable)
index 0000000..3f64ce7
--- /dev/null
@@ -0,0 +1,1099 @@
+#!/usr/bin/env python
+#
+# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import unittest
+import sys
+import os
+
+sys.path.append('../')
+from generation import *
+from pyparsing import *
+
+class GenerationModelTest(unittest.TestCase):
+
+    def setUp(self):
+        self.model = GenerationModel()
+        self.sdkconfig = None
+        self.sections_info = None
+        self.script_model = None
+
+        with open("data/Kconfig") as kconfig_file_obj:
+            with open("data/sdkconfig") as sdkconfig_file_obj:
+                self.sdkconfig = SDKConfig(kconfig_file_obj, sdkconfig_file_obj)
+
+        with open("data/sample.lf") as fragment_file_obj:
+            fragment_file = FragmentFileModel(fragment_file_obj)
+            self.model.add_fragments_from_file(fragment_file)
+
+        self.sections_info = SectionsInfo()
+
+        with open("data/sections.info") as sections_info_file_obj:
+            self.sections_info.add_sections_info(sections_info_file_obj)
+
+        with open("data/template.ld") as template_file_obj:
+            self.script_model = TemplateModel(template_file_obj)
+
+    def _add_mapping(self, text):
+        parser = Mapping.get_fragment_grammar()
+        fragment = parser.parseString(text, parseAll=True)
+        self.model.mappings[fragment[0].name] = fragment[0]
+
+    def _add_sections(self, text):
+        parser = Sections.get_fragment_grammar()
+        fragment = parser.parseString(text, parseAll=True)
+        self.model.sections[fragment[0].name] = fragment[0]
+
+    def _add_scheme(self, text):
+        parser = Scheme.get_fragment_grammar()
+        fragment = parser.parseString(text, parseAll=True)
+        self.model.schemes[fragment[0].name] = fragment[0]
+
+    def _write(self, expected, actual):
+        self.script_model.fill(expected, self.sdkconfig)
+        self.script_model.write(open("expected.ld", "w"))
+
+        self.script_model.fill(actual, self.sdkconfig)
+        self.script_model.write(open("actual.ld", "w"))
+
+    def _generate_default_rules(self):
+        rules = dict()
+
+        # flash_text
+        placement_rules = list()
+        rule = PlacementRule(None, None, None, self.model.sections["text"].entries, "flash_text")
+        placement_rules.append(rule)
+        rules["flash_text"] = placement_rules
+
+        # flash_rodata
+        placement_rules = list()
+        rule = PlacementRule(None, None, None, self.model.sections["rodata"].entries, "flash_rodata")
+        placement_rules.append(rule)
+        rules["flash_rodata"] = placement_rules
+
+        # dram0_data
+        placement_rules = list()
+        rule = PlacementRule(None, None, None, self.model.sections["data"].entries + self.model.sections["dram"].entries, "dram0_data")
+        placement_rules.append(rule)
+        rules["dram0_data"] = placement_rules
+
+        # dram0_bss
+        placement_rules = list()
+        rule = PlacementRule(None, None, None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "dram0_bss")
+        placement_rules.append(rule)
+        rules["dram0_bss"] = placement_rules
+
+        # iram0_text
+        placement_rules = list()
+        rule = PlacementRule(None, None, None, self.model.sections["iram"].entries, "iram0_text")
+        placement_rules.append(rule)
+        rules["iram0_text"] = placement_rules
+
+        # rtc_text
+        placement_rules = list()
+        rule = PlacementRule(None, None, None, self.model.sections["rtc_text"].entries, "rtc_text")
+        placement_rules.append(rule)
+        rules["rtc_text"] = placement_rules
+
+        # rtc_data
+        placement_rules = list()
+        rule = PlacementRule(None, None, None, self.model.sections["rtc_data"].entries + self.model.sections["rtc_rodata"].entries, "rtc_data")
+        placement_rules.append(rule)
+        rules["rtc_data"] = placement_rules
+
+        # rtc_bss
+        placement_rules = list()
+        rule = PlacementRule(None, None, None, self.model.sections["rtc_bss"].entries, "rtc_bss")
+        placement_rules.append(rule)
+        rules["rtc_bss"] = placement_rules
+
+        return rules
+
+    def _compare_rules(self, expected, actual):
+        self.assertEqual(set(expected.keys()), set(actual.keys()))
+
+        for (target, rules) in actual.items():
+
+            message = "target: " + target
+
+            actual_target_rules = rules
+            expected_target_rules = expected[target]
+
+            self.assertEqual(len(actual_target_rules), len(expected_target_rules))
+
+            for actual_target_rule in actual_target_rules:
+                self.assertTrue(actual_target_rule in expected_target_rules, message + str(actual_target_rule))
+
+            for expected_target_rule in expected_target_rules:
+                self.assertTrue(expected_target_rule in actual_target_rules, message + str(expected_target_rule))
+
+    def _get_default(self, target, rules):
+        return rules[target][0]
+
+    def test_rule_generation_blank(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        self.assertEqual(expected, actual)
+
+    def test_rule_generation_nominal_1(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            * (noflash)
+        """
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+
+        iram0_text_E1 = PlacementRule("libfreertos.a", "*", None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E1 = PlacementRule("libfreertos.a", "*", None, self.model.sections["rodata"].entries, "dram0_data")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E1)
+        flash_rodata_default.add_exclusion(dram0_data_E1)
+
+        # Add to the placement rules list
+        expected["iram0_text"].append(iram0_text_E1)
+        expected["dram0_data"].append(dram0_data_E1)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_2(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            timers (rtc)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        rtc_text_E1 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E1 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E1 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(rtc_text_E1)
+        flash_rodata_default.add_exclusion(rtc_data_E1)
+        dram0_data_default.add_exclusion(rtc_data_E1)
+        dram0_bss_default.add_exclusion(rtc_bss_E1)
+
+        # Add the rules
+        expected["rtc_text"].append(rtc_text_E1)
+        expected["rtc_data"].append(rtc_data_E1)
+        expected["rtc_bss"].append(rtc_bss_E1)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_3(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            timers (rtc)
+            * (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        rtc_text_E1 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E1 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E1 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E2 = PlacementRule("libfreertos.a", "*", None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E2 = PlacementRule("libfreertos.a", "*", None, self.model.sections["rodata"].entries, "dram0_data")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E2)
+        flash_rodata_default.add_exclusion(dram0_data_E2)
+
+        dram0_data_default.add_exclusion(rtc_data_E1)
+        dram0_bss_default.add_exclusion(rtc_bss_E1)
+
+        iram0_text_E2.add_exclusion(rtc_text_E1)
+        dram0_data_E2.add_exclusion(rtc_data_E1)
+
+        # Add the rules
+        expected["iram0_text"].append(iram0_text_E2)
+        expected["dram0_data"].append(dram0_data_E2)
+
+        expected["rtc_text"].append(rtc_text_E1)
+        expected["rtc_data"].append(rtc_data_E1)
+        expected["rtc_bss"].append(rtc_bss_E1)
+
+        self._compare_rules(expected, actual)
+
+
+    def test_rule_generation_nominal_4(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine (rtc)
+            event_groups (noflash)
+            timers (rtc)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        rtc_text_E1 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E1 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E1 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E2 = PlacementRule("libfreertos.a", "event_groups", None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E2 = PlacementRule("libfreertos.a", "event_groups", None, self.model.sections["rodata"].entries, "dram0_data")
+
+        rtc_text_E3 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E3 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E3 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(rtc_text_E3)
+        flash_rodata_default.add_exclusion(rtc_data_E3)
+        dram0_data_default.add_exclusion(rtc_data_E3)
+        dram0_bss_default.add_exclusion(rtc_bss_E3)
+
+        flash_text_default.add_exclusion(iram0_text_E2)
+        flash_rodata_default.add_exclusion(dram0_data_E2)
+
+        flash_text_default.add_exclusion(rtc_text_E1)
+        flash_rodata_default.add_exclusion(rtc_data_E1)
+        dram0_data_default.add_exclusion(rtc_data_E1)
+        dram0_bss_default.add_exclusion(rtc_bss_E1)
+
+        # Add the rules
+        expected["rtc_text"].append(rtc_text_E3)
+        expected["rtc_data"].append(rtc_data_E3)
+        expected["rtc_bss"].append(rtc_bss_E3)
+
+        expected["iram0_text"].append(iram0_text_E2)
+        expected["dram0_data"].append(dram0_data_E2)
+
+        expected["rtc_text"].append(rtc_text_E1)
+        expected["rtc_data"].append(rtc_data_E1)
+        expected["rtc_bss"].append(rtc_bss_E1)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_5(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine (rtc)
+            event_groups (noflash)
+            timers (rtc)
+            * (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        rtc_text_E1 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E1 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E1 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E2 = PlacementRule("libfreertos.a", "event_groups", None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E2 = PlacementRule("libfreertos.a", "event_groups", None, self.model.sections["rodata"].entries, "dram0_data")
+
+        rtc_text_E3 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E3 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E3 = PlacementRule("libfreertos.a", "timers", None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E4 = PlacementRule("libfreertos.a", "*", None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E4 = PlacementRule("libfreertos.a", "*", None, self.model.sections["rodata"].entries, "dram0_data")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E4)
+        flash_rodata_default.add_exclusion(dram0_data_E4)
+
+        iram0_text_E4.add_exclusion(rtc_text_E3)
+        dram0_data_E4.add_exclusion(rtc_data_E3)
+        dram0_data_default.add_exclusion(rtc_data_E3)
+        dram0_bss_default.add_exclusion(rtc_bss_E3)
+
+        iram0_text_E4.add_exclusion(iram0_text_E2)
+        dram0_data_E4.add_exclusion(dram0_data_E2)
+
+        iram0_text_E4.add_exclusion(rtc_text_E1)
+        dram0_data_E4.add_exclusion(rtc_data_E1)
+        dram0_data_default.add_exclusion(rtc_data_E1)
+        dram0_bss_default.add_exclusion(rtc_bss_E1)
+
+        # Add the rules
+        expected["iram0_text"].append(iram0_text_E4)
+        expected["dram0_data"].append(dram0_data_E4)
+
+        expected["rtc_text"].append(rtc_text_E3)
+        expected["rtc_data"].append(rtc_data_E3)
+        expected["rtc_bss"].append(rtc_bss_E3)
+
+        expected["iram0_text"].append(iram0_text_E2)
+        expected["dram0_data"].append(dram0_data_E2)
+
+        expected["rtc_text"].append(rtc_text_E1)
+        expected["rtc_data"].append(rtc_data_E1)
+        expected["rtc_bss"].append(rtc_bss_E1)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_6(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine:prvCheckPendingReadyList (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+
+        iram0_text_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckPendingReadyList", self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckPendingReadyList", self.model.sections["rodata"].entries, "dram0_data")
+
+        iram0_text_E1_extra = PlacementRule("libfreertos.a", "croutine", None, [".text.*", ".literal.*"], "flash_text")
+        dram0_data_E1_extra = PlacementRule("libfreertos.a", "croutine", None, [".rodata.*"], "flash_rodata")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E1_extra, self.sections_info)
+        flash_rodata_default.add_exclusion(dram0_data_E1_extra, self.sections_info)
+
+        iram0_text_E1_extra.add_exclusion(iram0_text_E1, self.sections_info)
+        dram0_data_E1_extra.add_exclusion(dram0_data_E1, self.sections_info)
+
+        # Add the rules
+        expected["flash_text"].append(iram0_text_E1_extra)
+        expected["flash_rodata"].append(dram0_data_E1_extra)
+
+        expected["iram0_text"].append(iram0_text_E1)
+        expected["dram0_data"].append(dram0_data_E1)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_7(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine:prvCheckPendingReadyList (noflash)
+            croutine:prvCheckDelayedList (noflash)
+            croutine:xCoRoutineCreate (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+
+        iram0_text_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckPendingReadyList", self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckPendingReadyList", self.model.sections["rodata"].entries, "dram0_data")
+
+        iram0_text_E2 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E2 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["rodata"].entries, "dram0_data")
+
+        iram0_text_E3 = PlacementRule("libfreertos.a", "croutine", "xCoRoutineCreate", self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E3 = PlacementRule("libfreertos.a", "croutine", "xCoRoutineCreate", self.model.sections["rodata"].entries, "dram0_data")
+
+        flash_text_extra = PlacementRule("libfreertos.a", "croutine", None, [".text.*", ".literal.*"], "flash_text")
+        flash_rodata_extra = PlacementRule("libfreertos.a", "croutine", None, [".rodata.*"], "flash_rodata")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(flash_text_extra, self.sections_info)
+        flash_rodata_default.add_exclusion(flash_rodata_extra, self.sections_info)
+
+        flash_text_extra.add_exclusion(iram0_text_E1, self.sections_info)
+        flash_rodata_extra.add_exclusion(dram0_data_E1, self.sections_info)
+
+        flash_text_extra.add_exclusion(iram0_text_E2, self.sections_info)
+        flash_rodata_extra.add_exclusion(dram0_data_E2, self.sections_info)
+
+        flash_text_extra.add_exclusion(iram0_text_E3, self.sections_info)
+        flash_rodata_extra.add_exclusion(dram0_data_E3, self.sections_info)
+
+        # Add the rules
+        expected["flash_text"].append(flash_text_extra)
+        expected["flash_rodata"].append(flash_rodata_extra)
+
+        expected["iram0_text"].append(iram0_text_E1)
+        expected["dram0_data"].append(dram0_data_E1)
+
+        expected["iram0_text"].append(iram0_text_E2)
+        expected["dram0_data"].append(dram0_data_E2)
+
+        expected["iram0_text"].append(iram0_text_E3)
+        expected["dram0_data"].append(dram0_data_E3)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_8(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine:prvCheckPendingReadyList (noflash)
+            croutine:prvCheckDelayedList (rtc)
+            croutine:xCoRoutineCreate (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        iram0_text_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckPendingReadyList", self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckPendingReadyList", self.model.sections["rodata"].entries, "dram0_data")
+
+        rtc_text_E2 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E2 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E2 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E3 = PlacementRule("libfreertos.a", "croutine", "xCoRoutineCreate", self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E3 = PlacementRule("libfreertos.a", "croutine", "xCoRoutineCreate", self.model.sections["rodata"].entries, "dram0_data")
+
+        flash_text_extra = PlacementRule("libfreertos.a", "croutine", None, [".text.*", ".literal.*"], "flash_text")
+        flash_rodata_extra = PlacementRule("libfreertos.a", "croutine", None, [".rodata.*"], "flash_rodata")
+        dram0_data_extra = PlacementRule("libfreertos.a", "croutine", None, [".data.*"], "dram0_data")
+        dram0_bss_extra = PlacementRule("libfreertos.a", "croutine", None, [".bss.*"], "dram0_bss")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(flash_text_extra, self.sections_info)
+        flash_rodata_default.add_exclusion(flash_rodata_extra, self.sections_info)
+        dram0_data_default.add_exclusion(dram0_data_extra, self.sections_info)
+        dram0_bss_default.add_exclusion(dram0_bss_extra, self.sections_info)
+
+        flash_text_extra.add_exclusion(iram0_text_E1, self.sections_info)
+        flash_rodata_extra.add_exclusion(dram0_data_E1, self.sections_info)
+
+        flash_text_extra.add_exclusion(rtc_text_E2, self.sections_info)
+        dram0_data_extra.add_exclusion(rtc_data_E2, self.sections_info)
+        flash_rodata_extra.add_exclusion(rtc_data_E2, self.sections_info)
+        dram0_bss_extra.add_exclusion(rtc_bss_E2, self.sections_info)
+
+        flash_text_extra.add_exclusion(iram0_text_E3, self.sections_info)
+        flash_rodata_extra.add_exclusion(dram0_data_E3, self.sections_info)
+
+        # Add the rules
+        expected["flash_text"].append(flash_text_extra)
+        expected["flash_rodata"].append(flash_rodata_extra)
+        expected["dram0_data"].append(dram0_data_extra)
+        expected["dram0_bss"].append(dram0_bss_extra)
+
+        expected["iram0_text"].append(iram0_text_E1)
+        expected["dram0_data"].append(dram0_data_E1)
+
+        expected["rtc_text"].append(rtc_text_E2)
+        expected["rtc_data"].append(rtc_data_E2)
+        expected["rtc_bss"].append(rtc_bss_E2)
+
+        expected["iram0_text"].append(iram0_text_E3)
+        expected["dram0_data"].append(dram0_data_E3)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_9(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine:prvCheckDelayedList (rtc)
+            croutine (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        rtc_text_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E2 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E2 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["rodata"].entries, "dram0_data")
+
+        dram0_data_extra = PlacementRule("libfreertos.a", "croutine", None, [".data.*"], "dram0_data")
+        dram0_bss_extra = PlacementRule("libfreertos.a", "croutine", None, [".bss.*"], "dram0_bss")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E2, self.sections_info)
+        flash_rodata_default.add_exclusion(dram0_data_E2, self.sections_info)
+
+        dram0_data_default.add_exclusion(dram0_data_extra, self.sections_info)
+        dram0_bss_default.add_exclusion(dram0_bss_extra, self.sections_info)
+
+        dram0_data_extra.add_exclusion(rtc_data_E1, self.sections_info)
+        dram0_bss_extra.add_exclusion(rtc_bss_E1, self.sections_info)
+
+        iram0_text_E2.add_exclusion(rtc_text_E1, self.sections_info)
+        dram0_data_E2.add_exclusion(rtc_data_E1, self.sections_info)
+
+        # Add the rules
+        expected["dram0_data"].append(dram0_data_extra)
+        expected["dram0_bss"].append(dram0_bss_extra)
+
+        expected["iram0_text"].append(iram0_text_E2)
+        expected["dram0_data"].append(dram0_data_E2)
+
+        expected["rtc_text"].append(rtc_text_E1)
+        expected["rtc_data"].append(rtc_data_E1)
+        expected["rtc_bss"].append(rtc_bss_E1)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_10(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine:prvCheckDelayedList (rtc)
+            * (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        iram0_text_default = self._get_default("iram0_text", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        rtc_text_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E2 = PlacementRule("libfreertos.a", None, None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E2 = PlacementRule("libfreertos.a", None, None, self.model.sections["rodata"].entries, "dram0_data")
+
+        iram0_text_extra = PlacementRule("libfreertos.a", "croutine", None, [".text.*", ".literal.*"], "iram0_text")
+        dram0_data_extra = PlacementRule("libfreertos.a", "croutine", None, [".data.*", ".rodata.*"], "dram0_data")
+        dram0_bss_extra = PlacementRule("libfreertos.a", "croutine", None, [".bss.*"], "dram0_bss")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E2, self.sections_info)
+        flash_rodata_default.add_exclusion(dram0_data_E2, self.sections_info)
+
+        iram0_text_default.add_exclusion(iram0_text_extra, self.sections_info)
+        dram0_data_default.add_exclusion(dram0_data_extra, self.sections_info)
+        dram0_bss_default.add_exclusion(dram0_bss_extra, self.sections_info)
+
+        iram0_text_E2.add_exclusion(iram0_text_extra, self.sections_info)
+        dram0_data_E2.add_exclusion(dram0_data_extra, self.sections_info)
+
+        iram0_text_extra.add_exclusion(rtc_text_E1, self.sections_info)
+        dram0_data_extra.add_exclusion(rtc_data_E1, self.sections_info)
+        dram0_bss_extra.add_exclusion(rtc_bss_E1, self.sections_info)
+
+        # Add the rules
+        expected["iram0_text"].append(iram0_text_extra)
+        expected["dram0_data"].append(dram0_data_extra)
+        expected["dram0_bss"].append(dram0_bss_extra)
+
+        expected["iram0_text"].append(iram0_text_E2)
+        expected["dram0_data"].append(dram0_data_E2)
+
+        expected["rtc_text"].append(rtc_text_E1)
+        expected["rtc_data"].append(rtc_data_E1)
+        expected["rtc_bss"].append(rtc_bss_E1)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_11(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine:prvCheckDelayedList (noflash)
+            croutine (rtc)
+            * (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        iram0_text_default = self._get_default("iram0_text", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        iram0_text_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["rodata"].entries, "dram0_data")
+
+        rtc_text_E2 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E2 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E2 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E3 = PlacementRule("libfreertos.a", None, None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E3 = PlacementRule("libfreertos.a", None, None, self.model.sections["rodata"].entries, "dram0_data")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E3, self.sections_info)
+        flash_rodata_default.add_exclusion(dram0_data_E3, self.sections_info)
+        iram0_text_default.add_exclusion(rtc_text_E2, self.sections_info)
+        dram0_data_default.add_exclusion(rtc_data_E2, self.sections_info)
+        dram0_bss_default.add_exclusion(rtc_bss_E2, self.sections_info)
+
+        iram0_text_E3.add_exclusion(rtc_text_E2, self.sections_info)
+        dram0_data_E3.add_exclusion(rtc_data_E2, self.sections_info)
+
+        rtc_text_E2.add_exclusion(iram0_text_E1, self.sections_info)
+        rtc_data_E2.add_exclusion(dram0_data_E1, self.sections_info)
+
+        # Add the rules
+        expected["iram0_text"].append(iram0_text_E1)
+        expected["dram0_data"].append(dram0_data_E1)
+
+        expected["rtc_text"].append(rtc_text_E2)
+        expected["rtc_data"].append(rtc_data_E2)
+        expected["rtc_bss"].append(rtc_bss_E2)
+
+        expected["iram0_text"].append(iram0_text_E3)
+        expected["dram0_data"].append(dram0_data_E3)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_12(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine:prvCheckDelayedList (rtc)
+            croutine (noflash)
+            * (rtc)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        rtc_text_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E2 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E2 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["rodata"].entries, "dram0_data")
+
+        rtc_text_E3 = PlacementRule("libfreertos.a", None, None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E3 = PlacementRule("libfreertos.a", None, None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E3 = PlacementRule("libfreertos.a", None, None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        rtc_data_extra = PlacementRule("libfreertos.a", "croutine", None, [".data.*"], "rtc_data")
+        rtc_bss_extra = PlacementRule("libfreertos.a", "croutine", None, [".bss.*"], "rtc_bss")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(rtc_text_E3, self.sections_info)
+        flash_rodata_default.add_exclusion(rtc_data_E3, self.sections_info)
+        dram0_data_default.add_exclusion(rtc_data_E3, self.sections_info)
+        dram0_bss_default.add_exclusion(rtc_bss_E3, self.sections_info)
+
+        rtc_text_E3.add_exclusion(iram0_text_E2, self.sections_info)
+        rtc_data_E3.add_exclusion(dram0_data_E2, self.sections_info)
+        rtc_data_E3.add_exclusion(rtc_data_extra, self.sections_info)
+        rtc_bss_E3.add_exclusion(rtc_bss_extra, self.sections_info)
+
+        rtc_data_extra.add_exclusion(rtc_data_E1, self.sections_info)
+        rtc_bss_extra.add_exclusion(rtc_bss_E1, self.sections_info)
+        iram0_text_E2.add_exclusion(rtc_text_E1, self.sections_info)
+        dram0_data_E2.add_exclusion(rtc_data_E1, self.sections_info)
+
+        # Add the rules
+        expected["rtc_data"].append(rtc_data_extra)
+        expected["rtc_bss"].append(rtc_bss_extra)
+
+        expected["rtc_text"].append(rtc_text_E1)
+        expected["rtc_data"].append(rtc_data_E1)
+        expected["rtc_bss"].append(rtc_bss_E1)
+
+        expected["iram0_text"].append(iram0_text_E2)
+        expected["dram0_data"].append(dram0_data_E2)
+
+        expected["rtc_text"].append(rtc_text_E3)
+        expected["rtc_data"].append(rtc_data_E3)
+        expected["rtc_bss"].append(rtc_bss_E3)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_13(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine:prvCheckDelayedList (noflash)
+            event_groups:xEventGroupCreate (noflash)
+            croutine (rtc)
+            event_groups (rtc)
+            * (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        iram0_text_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["rodata"].entries, "dram0_data")
+
+        iram0_text_E2 = PlacementRule("libfreertos.a", "event_groups", "xEventGroupCreate", self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E2 = PlacementRule("libfreertos.a", "event_groups", "xEventGroupCreate", self.model.sections["rodata"].entries, "dram0_data")
+
+        rtc_text_E3 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E3 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E3 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        rtc_text_E4 = PlacementRule("libfreertos.a", "event_groups", None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E4 = PlacementRule("libfreertos.a", "event_groups", None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E4 = PlacementRule("libfreertos.a", "event_groups", None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E5 = PlacementRule("libfreertos.a", None, None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E5 = PlacementRule("libfreertos.a", None, None, self.model.sections["rodata"].entries, "dram0_data")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E5, self.sections_info)
+        flash_rodata_default.add_exclusion(dram0_data_E5, self.sections_info)
+        dram0_bss_default.add_exclusion(rtc_bss_E3, self.sections_info)
+        dram0_data_default.add_exclusion(rtc_data_E3, self.sections_info)
+        dram0_bss_default.add_exclusion(rtc_bss_E4, self.sections_info)
+        dram0_data_default.add_exclusion(rtc_data_E4, self.sections_info)
+
+        iram0_text_E5.add_exclusion(rtc_text_E3, self.sections_info)
+        dram0_data_E5.add_exclusion(rtc_data_E3, self.sections_info)
+        iram0_text_E5.add_exclusion(rtc_text_E4, self.sections_info)
+        dram0_data_E5.add_exclusion(rtc_data_E4, self.sections_info)
+
+        rtc_text_E4.add_exclusion(iram0_text_E2, self.sections_info)
+        rtc_data_E4.add_exclusion(dram0_data_E2, self.sections_info)
+
+        rtc_text_E3.add_exclusion(iram0_text_E1, self.sections_info)
+        rtc_data_E3.add_exclusion(dram0_data_E1, self.sections_info)
+
+        # Add the rules
+        expected["iram0_text"].append(iram0_text_E1)
+        expected["dram0_data"].append(dram0_data_E1)
+
+        expected["iram0_text"].append(iram0_text_E2)
+        expected["dram0_data"].append(dram0_data_E2)
+
+        expected["rtc_text"].append(rtc_text_E3)
+        expected["rtc_data"].append(rtc_data_E3)
+        expected["rtc_bss"].append(rtc_bss_E3)
+
+        expected["rtc_text"].append(rtc_text_E4)
+        expected["rtc_data"].append(rtc_data_E4)
+        expected["rtc_bss"].append(rtc_bss_E4)
+
+        expected["iram0_text"].append(iram0_text_E5)
+        expected["dram0_data"].append(dram0_data_E5)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_14(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine:prvCheckDelayedList (noflash)
+            event_groups:xEventGroupCreate (rtc)
+            croutine (rtc)
+            event_groups (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+        dram0_data_default = self._get_default("dram0_data", expected)
+        dram0_bss_default = self._get_default("dram0_bss", expected)
+
+        iram0_text_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E1 = PlacementRule("libfreertos.a", "croutine", "prvCheckDelayedList", self.model.sections["rodata"].entries, "dram0_data")
+
+        rtc_text_E2 = PlacementRule("libfreertos.a", "event_groups", "xEventGroupCreate", self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E2 = PlacementRule("libfreertos.a", "event_groups", "xEventGroupCreate", self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E2 = PlacementRule("libfreertos.a", "event_groups", "xEventGroupCreate", self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        rtc_text_E3 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["text"].entries, "rtc_text")
+        rtc_data_E3 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["data"].entries + self.model.sections["rodata"].entries, "rtc_data")
+        rtc_bss_E3 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["bss"].entries + self.model.sections["common"].entries, "rtc_bss")
+
+        iram0_text_E4 = PlacementRule("libfreertos.a", "event_groups", None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E4 = PlacementRule("libfreertos.a", "event_groups", None, self.model.sections["rodata"].entries, "dram0_data")
+
+        dram0_data_extra = PlacementRule("libfreertos.a", "event_groups", None, [".data.*"], "dram0_data")
+        dram0_bss_extra = PlacementRule("libfreertos.a", "event_groups", None, [".bss.*"], "dram0_bss")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E4, self.sections_info)
+        flash_rodata_default.add_exclusion(dram0_data_E4, self.sections_info)
+        dram0_data_default.add_exclusion(dram0_data_extra, self.sections_info)
+        dram0_bss_default.add_exclusion(dram0_bss_extra, self.sections_info)
+
+        flash_text_default.add_exclusion(rtc_text_E3, self.sections_info)
+        flash_rodata_default.add_exclusion(rtc_data_E3, self.sections_info)
+        dram0_data_default.add_exclusion(rtc_data_E3, self.sections_info)
+        dram0_bss_default.add_exclusion(rtc_bss_E3, self.sections_info)
+
+        iram0_text_E4.add_exclusion(rtc_text_E2, self.sections_info)
+        dram0_data_E4.add_exclusion(rtc_data_E2, self.sections_info)
+        dram0_data_extra.add_exclusion(rtc_data_E2, self.sections_info)
+        dram0_bss_extra.add_exclusion(rtc_bss_E2, self.sections_info)
+
+        rtc_text_E3.add_exclusion(iram0_text_E1, self.sections_info)
+        rtc_data_E3.add_exclusion(dram0_data_E1, self.sections_info)
+
+        # Add the rules
+        expected["iram0_text"].append(iram0_text_E1)
+        expected["dram0_data"].append(dram0_data_E1)
+
+        expected["rtc_text"].append(rtc_text_E2)
+        expected["rtc_data"].append(rtc_data_E2)
+        expected["rtc_bss"].append(rtc_bss_E2)
+
+        expected["rtc_text"].append(rtc_text_E3)
+        expected["rtc_data"].append(rtc_data_E3)
+        expected["rtc_bss"].append(rtc_bss_E3)
+
+        expected["iram0_text"].append(iram0_text_E4)
+        expected["dram0_data"].append(dram0_data_E4)
+
+        expected["dram0_data"].append(dram0_data_extra)
+        expected["dram0_bss"].append(dram0_bss_extra)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_15(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine (noflash_data)
+            croutine (noflash_text)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+
+        iram0_text_E1 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E1 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["rodata"].entries, "dram0_data")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E1)
+        flash_rodata_default.add_exclusion(dram0_data_E1)
+
+        # Add the rules
+        expected["iram0_text"].append(iram0_text_E1)
+        expected["dram0_data"].append(dram0_data_E1)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_nominal_16(self):
+        normal = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine (noflash_data)
+            croutine (noflash)
+        """
+
+        self._add_mapping(normal)
+
+        actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+        expected = self._generate_default_rules()
+
+        flash_text_default = self._get_default("flash_text", expected)
+        flash_rodata_default = self._get_default("flash_rodata", expected)
+
+        iram0_text_E1 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["text"].entries, "iram0_text")
+        dram0_data_E1 = PlacementRule("libfreertos.a", "croutine", None, self.model.sections["rodata"].entries, "dram0_data")
+
+        # Add the exclusions
+        flash_text_default.add_exclusion(iram0_text_E1)
+        flash_rodata_default.add_exclusion(dram0_data_E1)
+
+        # Add the rules
+        expected["iram0_text"].append(iram0_text_E1)
+        expected["dram0_data"].append(dram0_data_E1)
+
+        self._compare_rules(expected, actual)
+
+    def test_rule_generation_conflict(self):
+        conflict_mapping = """
+        [mapping]
+        archive: libfreertos.a
+        entries:
+            croutine (conflict)
+            croutine (noflash)
+        """
+
+        conflict_scheme =  """
+        [scheme:conflict]
+        entries:
+            rodata -> dram0_data
+            bss -> dram0_data
+        """
+
+        self._add_scheme(conflict_scheme)
+        self._add_mapping(conflict_mapping)
+
+        with self.assertRaises(GenerationException):
+            actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+
+    def test_rule_generation_condition (self):
+        generation_with_condition = """
+        [mapping]
+        archive: lib.a
+        entries:
+            : PERFORMANCE_LEVEL = 0
+            : PERFORMANCE_LEVEL = 1
+            obj1 (noflash)
+            : PERFORMANCE_LEVEL = 2
+            obj1 (noflash)
+            obj2 (noflash)
+            : PERFORMANCE_LEVEL = 3
+            obj1 (noflash)
+            obj2 (noflash)
+            obj3 (noflash)
+        """
+
+        self._add_mapping(generation_with_condition)
+
+        for perf_level in range(0, 4):
+            self.sdkconfig.config.syms["PERFORMANCE_LEVEL"].set_value(str(perf_level))
+
+            actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
+            expected = self._generate_default_rules()
+
+            flash_text_default = self._get_default("flash_text", expected)
+            flash_rodata_default = self._get_default("flash_rodata", expected)
+
+            if perf_level < 4:
+                for append_no in range (1, perf_level + 1):
+                    iram_rule = PlacementRule("lib.a", "obj" + str(append_no), None, self.model.sections["text"].entries, "iram0_text")
+                    dram_rule = PlacementRule("lib.a", "obj" + str(append_no), None, self.model.sections["rodata"].entries, "dram0_data")
+
+                    flash_text_default.add_exclusion(iram_rule)
+                    flash_rodata_default.add_exclusion(dram_rule)
+
+                    expected["iram0_text"].append(iram_rule)
+                    expected["dram0_data"].append(dram_rule)
+
+            self._compare_rules(expected, actual)
+
+if __name__ =="__main__":
+    unittest.main()
\ No newline at end of file