binutils-gdb/ld/testsuite/ld-arm/stm32l4xx-fix-ldm.s
Laurent Alfonsi a504d23a83 Add --fix-stm32l4xx-629360 to the ARM linker to enable a link-time workaround for a bug in the bus matrix / memory controller for some of the STM32 Cortex-M4 based products (STM32L4xx).
bfd  * bfd-in2.h: Regenerate.
     * bfd-in.h (bfd_arm_stm32l4xx_fix): New enum. Specify how
     STM32L4XX instruction scanning should be done.
     (bfd_elf32_arm_set_stm32l4xx_fix)
     (bfd_elf32_arm_stm32l4xx_erratum_scan)
     (bfd_elf32_arm_stm32l4xx_fix_veneer_locations): Add prototypes.
     (bfd_elf32_arm_set_target_relocs): Add stm32l4xx fix type argument
     to prototype.
     * elf32-arm.c (STM32L4XX_ERRATUM_VENEER_SECTION_NAME)
     (STM32L4XX_ERRATUM_VENEER_ENTRY_NAME): Define macros.
     (elf32_stm32l4xx_erratum_type): New enum.
     (elf32_stm32l4xx_erratum_list): New struct. List of veneers or
     jumps to veneers.
     (_arm_elf_section_data): Add stm32l4xx_erratumcount,
     stm32l4xx_erratumlist.
     (elf32_arm_link_hash_table): Add stm32l4xx_erratum_glue_size,
     stm32l4xx_fix and num_stm32l4xx_fixes fields.
     (ctz): New function.
     (popcount): New function.
     (elf32_arm_link_hash_table_create): Initialize stm32l4xx_fix.
     (put_thumb2_insn): New function.
     (STM32L4XX_ERRATUM_LDM_VENEER_SIZE): Define. Size of a veneer for
     LDM instructions.
     (STM32L4XX_ERRATUM_VLDM_VENEER_SIZE): Define. Size of a veneer for
     VLDM instructions.
     (bfd_elf32_arm_allocate_interworking_sections): Initialise erratum
     glue section.
     (record_stm32l4xx_erratum_veneer) : New function. Create a single
     veneer, and its associated symbols.
     (bfd_elf32_arm_add_glue_sections_to_bfd): Add STM32L4XX erratum glue.
     (bfd_elf32_arm_set_stm32l4xx_fix): New function. Set the type of
     erratum workaround required.
     (bfd_elf32_arm_stm32l4xx_fix_veneer_locations): New function. Find
     out where veneers and branches to veneers have been placed in
     virtual memory after layout.
     (is_thumb2_ldmia): New function.
     (is_thumb2_ldmdb): Likewise.
     (is_thumb2_vldm ): Likewise.
     (stm32l4xx_need_create_replacing_stub): New function. Decide if a
     veneer must be emitted.
     (bfd_elf32_arm_stm32l4xx_erratum_scan): Scan the sections of an
     input BFD for potential erratum-triggering insns. Record results.
     (bfd_elf32_arm_set_target_relocs): Set stm32l4xx_fix field in
     global hash table.
     (elf32_arm_size_dynamic_sections): Collect glue information.
     (create_instruction_branch_absolute): New function.
     (create_instruction_ldmia): Likewise.
     (create_instruction_ldmdb): Likewise.
     (create_instruction_mov): Likewise.
     (create_instruction_sub): Likewise.
     (create_instruction_vldmia): Likewise.
     (create_instruction_vldmdb): Likewise.
     (create_instruction_udf_w): Likewise.
     (create_instruction_udf): Likewise.
     (push_thumb2_insn32): Likewise.
     (push_thumb2_insn16): Likewise.
     (stm32l4xx_fill_stub_udf): Likewise.
     (stm32l4xx_create_replacing_stub_ldmia): New function. Expands the
     replacing stub for ldmia instructions.
     (stm32l4xx_create_replacing_stub_ldmdb): Likewise for ldmdb.
     (stm32l4xx_create_replacing_stub_vldm): Likewise for vldm.
     (stm32l4xx_create_replacing_stub): New function. Dispatches the
     stub emission to the appropriate functions.
     (elf32_arm_write_section): Output veneers, and branches to veneers.

ld   * ld.texinfo: Description of the STM32L4xx erratum workaround.
     * emultempl/armelf.em (stm32l4xx_fix): New.
     (arm_elf_before_allocation): Choose the type of fix, scan for
     erratum.
     (gld${EMULATION_NAME}_finish): Fix veneer locations.
     (arm_elf_create_output_section_statements): Propagate
     stm32l4xx_fix value.
     (PARSE_AND_LIST_PROLOGUE): Define OPTION_STM32L4XX_FIX.
     (PARSE_AND_LIST_LONGOPTS): Add entry for handling
     --fix-stm32l4xx-629360.
     (PARSE_AND_LIST_OPTION): Add entry for helping on
     --fix-stm32l4xx-629360.
     (PARSE_AND_LIST_ARGS_CASES): Treat OPTION_STM32L4XX_FIX.

tests * ld-arm/arm-elf.exp (armelftests_common): Add STM32L4XX
       tests.
     * ld-arm/stm32l4xx-cannot-fix-far-ldm.d: New.
     * ld-arm/stm32l4xx-cannot-fix-far-ldm.s: Likewise.
     * ld-arm/stm32l4xx-cannot-fix-it-block.d: Likewise.
     * ld-arm/stm32l4xx-cannot-fix-it-block.s: Likewise.
     * ld-arm/stm32l4xx-fix-all.d: Likewise.
     * ld-arm/stm32l4xx-fix-all.s: Likewise.
     * ld-arm/stm32l4xx-fix-it-block.d: Likewise.
     * ld-arm/stm32l4xx-fix-it-block.s: Likewise.
     * ld-arm/stm32l4xx-fix-ldm.d: Likewise.
     * ld-arm/stm32l4xx-fix-ldm.s: Likewise.
     * ld-arm/stm32l4xx-fix-vldm.d: Likewise.
     * ld-arm/stm32l4xx-fix-vldm.s: Likewise.
2015-10-27 13:20:33 +00:00

148 lines
4.7 KiB
ArmAsm

.syntax unified
.cpu cortex-m4
.fpu fpv4-sp-d16
.text
.align 1
.thumb
.thumb_func
.global _start
_start:
@ LDM CASE #1 (used when rx is in upper_list)
@ ldm rx, {...} ->
@ ldm rx!, {lower_list}
@ ldm rx, {upper_list}
@ b.w
ldm.w r9, {r1-r9}
@ LDM CASE #1 bis (used when rx is in upper_list and pc is
@ in reglist)
@ ldm rx, {...} ->
@ ldm rx!, {lower_list}
@ ldm rx, {upper_list}
ldm.w r9, {r1-r9, pc}
@ LDM CASE #2 (used when rx is not in upper_list)
@ ldm rx, {...} ->
@ mov ry, rx where ry is the lowest register from upper_list
@ ldm ry!, {lower_list}
@ ldm ry, {upper_list}
@ b.w
ldm.w r0, {r1-r9}
@ LDM CASE #2 bis (used when rx is in lower_list)
@ ldm rx, {...} ->
@ mov ry, rx where ry is the lowest register from upper_list
@ ldm ry!, {lower_list}
@ ldm ry, {upper_list}
@ b.w
ldm.w r1, {r1-r9}
@ LDM CASE #2 ter (used when rx is not in upper_list and pc is
@ in reglist)
@ ldm rx, {...} ->
@ mov ry, rx where ry is the lowest register from upper_list
@ ldm ry!, {lower_list}
@ ldm ry, {upper_list}
ldm.w r0, {r1-r9, pc}
@ LDM CASE #2 quater (used when rx is in lower_list and pc is
@ in reglist)
@ ldm rx, {...} ->
@ mov ry, rx where ry is the lowest register from upper_list
@ ldm ry!, {lower_list}
@ ldm ry, {upper_list}
ldm.w r1, {r1-r9, pc}
@ LDM CASE #3 (used when rx is not in upper_list)
@ ldm rx, {...} ->
@ ldm rx!, {lower_list}
@ ldm rx!, {upper_list}
@ b.w
@ Write-back variant are unpredictable when rx appears also in
@ the loaded registers
ldm.w r0!, {r1-r9}
@ LDM CASE #3 bis (used when rx is not in upper_list and pc is
@ in reglist)
@ ldm rx, {...} ->
@ ldm rx!, {lower_list}
@ ldm rx!, {upper_list}
ldm.w r0!, {r1-r9, pc}
@ LDM CASE #4 (used when pc is not in reglist and rx is in
@ lower_list)
@ ldmb rx, {...} ->
@ ldmb rx!, {upper_list}
@ ldmb rx, {lower_list}
ldmdb.w r1, {r1-r9}
@ LDM CASE #5 (used when pc is not in reglist and rx is not in
@ lower_list)
@ It looks like it this mean that it could be in upper_list or not
@ ldmdb rx, {...} ->
@ mov ry, rx where ry is the lowest register from lower_list
@ ldmdb ry!, {upper_list}
@ ldmdb ry , {lower_list}
@ b.w
ldmdb.w sl, {r1-r9}
@ LDM CASE #5 bis (used when pc is not in reglist and rx is in
@ upper_list)
@ ldmdb rx, {...} ->
@ mov ry, rx where ry is the lowest register from lower_list
@ ldmdb ry!, {upper_list}
@ ldmdb ry , {lower_list}
@ b.w
ldmdb.w r9, {r1-r9}
@ LDM CASE #6 (used when pc is in reglist and rx is in
@ upper_list)
@ ldmdb rx, {...} ->
@ sub rx, rx, #size (lower_list + upper_list)
@ ldm rx!, {lower_list}
@ ldm rx, {upper_list}
@ This case reverses the load order
ldmdb.w r9, {r1-r9, pc}
@ LDM CASE #6 bis (used when pc is in reglist and rx is in
@ lower_list)
@ ldmdb rx, {...} ->
@ sub rx, rx, #size (lower_list + upper_list)
@ ldm rx!, {lower_list}
@ ldm rx, {upper_list}
ldmdb.w r1, {r1-r9, pc}
@ LDM CASE #7 (used when pc is in reglist and rx is not in
@ upper_list)
@ ldmdb rx, {...} ->
@ sub ry, rx, #size (lower_list + upper_list) where ry is the lowest
@ register of the upper list
@ ldm ry!, {lower_list}
@ ldm ry , {upper_list}
@ This case reverses the load order
ldmdb.w r0, {r1-r9, pc}
@ LDM CASE #8 (used when pc is in not in reglist)
@ ldmdb rx!, {...} ->
@ ldm rx!, {upper_list}
@ ldm rx!, {lower_list}
@ b.w
ldmdb.w r0!, {r1-r9}
@ LDM CASE #9 (Used when pc is in reglist)
@ ldmdb rx!, {...} ->
@ sub rx, rx, #size (lower_list + upper_list)
@ mov ry, rx where ry is the lowest register from upper_list
@ ldm ry!, {lower_list}
@ ldm ry , {upper_list}
ldmdb.w r0!, {r1-r9, pc}
@ POP CASE #1 (list does not include pc)
@ pop {...} -> pop {lower_list} pop {upper_list}
@ b.w
pop {r0-r9}
@ POP CASE #2 (list includes PC)
@ pop {...} -> pop {lower_list} pop {upper_list}
pop {r0-r9, pc}