X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=ld%2Femultempl%2Fmsp430.em;h=c823a6d9b5868846ce3c00f662462bb27c5741c6;hb=a435742a7fb32f6320ce0e6074e2500e28378104;hp=22e7c42b11736bd2a930dda0357f8271307dd313;hpb=e1fa0163505af867009ea73fc5f705162120e795;p=deliverable%2Fbinutils-gdb.git diff --git a/ld/emultempl/msp430.em b/ld/emultempl/msp430.em index 22e7c42b11..c823a6d9b5 100644 --- a/ld/emultempl/msp430.em +++ b/ld/emultempl/msp430.em @@ -4,10 +4,10 @@ fragment < + +enum regions +{ + REGION_NONE = 0, + REGION_LOWER, + REGION_UPPER, + REGION_EITHER = 3, +}; + +enum either_placement_stage +{ + LOWER_TO_UPPER, + UPPER_TO_LOWER, +}; + +enum { ROM, RAM }; + +static int data_region = REGION_NONE; +static int code_region = REGION_NONE; +static bfd_boolean disable_sec_transformation = FALSE; + +#define MAX_PREFIX_LENGTH 7 EOF @@ -124,6 +149,32 @@ fi if test x"$LDEMUL_PLACE_ORPHAN" != xgld"$EMULATION_NAME"_place_orphan; then fragment <type) + { + case QUAD: + case SQUAD: + size = QUAD_SIZE; + break; + case LONG: + size = LONG_SIZE; + break; + case SHORT: + size = SHORT_SIZE; + break; + case BYTE: + size = BYTE_SIZE; + break; + default: + einfo (_("%P: error: unhandled data_statement size\n")); + FAIL (); + } + return size; +} + /* Helper function for place_orphan that computes the size of sections already mapped to the given statement. */ @@ -143,10 +194,15 @@ scan_children (lang_statement_union_type * l) case lang_constructors_statement_enum: case lang_assignment_statement_enum: + case lang_padding_statement_enum: break; case lang_wild_statement_enum: - amount += scan_children (l->wild_statement.children.head); + amount += scan_children (l->wild_statement.children.head); + break; + + case lang_data_statement_enum: + amount += data_statement_size (&l->data_statement); break; default: @@ -159,7 +215,41 @@ scan_children (lang_statement_union_type * l) return amount; } - + +#define WARN_UPPER 0 +#define WARN_LOWER 1 +#define WARN_TEXT 0 +#define WARN_DATA 1 +#define WARN_BSS 2 +#define WARN_RODATA 3 + +/* Warn only once per output section. + * NAME starts with ".upper." or ".lower.". */ +static void +warn_no_output_section (const char *name) +{ + static bfd_boolean warned[2][4] = {{FALSE, FALSE, FALSE, FALSE}, + {FALSE, FALSE, FALSE, FALSE}}; + int i = WARN_LOWER; + + if (strncmp (name, ".upper.", 7) == 0) + i = WARN_UPPER; + + if (!warned[i][WARN_TEXT] && strcmp (name + 6, ".text") == 0) + warned[i][WARN_TEXT] = TRUE; + else if (!warned[i][WARN_DATA] && strcmp (name + 6, ".data") == 0) + warned[i][WARN_DATA] = TRUE; + else if (!warned[i][WARN_BSS] && strcmp (name + 6, ".bss") == 0) + warned[i][WARN_BSS] = TRUE; + else if (!warned[i][WARN_RODATA] && strcmp (name + 6, ".rodata") == 0) + warned[i][WARN_RODATA] = TRUE; + else + return; + einfo ("%P: warning: no input section rule matches %s in linker script\n", + name); +} + + /* Place an orphan section. We use this to put .either sections into either their lower or their upper equivalents. */ @@ -174,7 +264,6 @@ gld${EMULATION_NAME}_place_orphan (asection * s, char * buf = NULL; lang_output_section_statement_type * lower; lang_output_section_statement_type * upper; - lang_output_section_statement_type * os; if ((s->flags & SEC_ALLOC) == 0) return NULL; @@ -186,6 +275,13 @@ gld${EMULATION_NAME}_place_orphan (asection * s, if (constraint != 0) return NULL; + if (strncmp (secname, ".upper.", 7) == 0 + || strncmp (secname, ".lower.", 7) == 0) + { + warn_no_output_section (secname); + return NULL; + } + /* We only need special handling for .either sections. */ if (strncmp (secname, ".either.", 8) != 0) return NULL; @@ -204,69 +300,521 @@ gld${EMULATION_NAME}_place_orphan (asection * s, } else name = (char *) secname; - + lower_name = concat (".lower", name, NULL); upper_name = concat (".upper", name, NULL); /* Find the corresponding lower and upper sections. */ lower = lang_output_section_find (lower_name); upper = lang_output_section_find (upper_name); - /* If the upper section does not exist, try again without the suffix. */ - if (upper == NULL) - upper = lang_output_section_find (name); - if (lower == NULL) + if (lower == NULL && upper == NULL) + { + einfo (_("%P: error: no section named %s or %s in linker script\n"), + lower_name, upper_name); + goto end; + } + else if (lower == NULL) { - os = upper; - if (upper == NULL) - { - einfo ("%P: error: no section named %s or %s in linker script\n", lower_name, upper_name); + lower = lang_output_section_find (name); + if (lower == NULL) + { + einfo (_("%P: error: no section named %s in linker script\n"), name); goto end; } } - else if (upper == NULL) - os = lower; - else if (lower->region == NULL) - os = lower; - /* If the section is too big for the region containing - the lower section then do not even try to use it. */ - else if (lower->region->length < s->size) - os = upper; + + /* Always place orphaned sections in lower. Optimal placement of either + sections is performed later, once section sizes have been finalized. */ + lang_add_section (& lower->children, s, NULL, lower); + end: + free (upper_name); + free (lower_name); + free (buf); + return lower; +} +EOF +fi + +fragment <header.type) + { + case lang_input_section_enum: + is = curr->input_section.section; + if (is == s) + { + s->output_section = NULL; + lang_add_section (& (new_output_section->children), s, NULL, + new_output_section); + /* Remove the section from the old output section. */ + if (prev == NULL) + *head = curr->header.next; + else + prev->header.next = curr->header.next; + return TRUE; + } + break; + case lang_wild_statement_enum: + if (change_output_section (&(curr->wild_statement.children.head), + s, new_output_section)) + return TRUE; + break; + default: + break; + } + prev = curr; + curr = curr->header.next; + } + return FALSE; +} + +static void +add_region_prefix (bfd *abfd ATTRIBUTE_UNUSED, asection *s, + void *unused ATTRIBUTE_UNUSED) +{ + const char *curr_name = bfd_section_name (s); + int region = REGION_NONE; + + if (strncmp (curr_name, ".text", 5) == 0) + region = code_region; + else if (strncmp (curr_name, ".data", 5) == 0) + region = data_region; + else if (strncmp (curr_name, ".bss", 4) == 0) + region = data_region; + else if (strncmp (curr_name, ".rodata", 7) == 0) + region = data_region; else + return; + + switch (region) { - bfd_size_type amount = 0; - struct lang_output_section_statement_struct * p; + case REGION_NONE: + break; + case REGION_UPPER: + bfd_rename_section (s, concat (".upper", curr_name, NULL)); + break; + case REGION_LOWER: + bfd_rename_section (s, concat (".lower", curr_name, NULL)); + break; + case REGION_EITHER: + s->name = concat (".either", curr_name, NULL); + break; + default: + /* Unreachable. */ + FAIL (); + break; + } +} - amount += scan_children (lower->children.head); +static void +msp430_elf_after_open (void) +{ + bfd *abfd; + + gld${EMULATION_NAME}_after_open (); - /* Also check forwards for other statements assigned to the same region. */ - for (p = lower->next; p != NULL; p = p->next) - if (p->region == lower->region) - amount += scan_children (p->children.head); + /* If neither --code-region or --data-region have been passed, do not + transform sections names. */ + if ((code_region == REGION_NONE && data_region == REGION_NONE) + || disable_sec_transformation) + return; - /* Scan backwards as well. */ - for (p = lower->prev; p != NULL; p = p->prev) - if (p->region == lower->region) - amount += scan_children (p->children.head); + for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link.next) + bfd_map_over_sections (abfd, add_region_prefix, NULL); +} - if (amount + s->size >= lower->region->length) - os = upper; +#define OPTION_CODE_REGION 321 +#define OPTION_DATA_REGION (OPTION_CODE_REGION + 1) +#define OPTION_DISABLE_TRANS (OPTION_CODE_REGION + 2) + +static void +gld${EMULATION_NAME}_add_options + (int ns, char **shortopts, int nl, struct option **longopts, + int nrl ATTRIBUTE_UNUSED, struct option **really_longopts ATTRIBUTE_UNUSED) +{ + static const char xtra_short[] = { }; + + static const struct option xtra_long[] = + { + { "code-region", required_argument, NULL, OPTION_CODE_REGION }, + { "data-region", required_argument, NULL, OPTION_DATA_REGION }, + { "disable-sec-transformation", no_argument, NULL, + OPTION_DISABLE_TRANS }, + { NULL, no_argument, NULL, 0 } + }; + + *shortopts = (char *) xrealloc (*shortopts, ns + sizeof (xtra_short)); + memcpy (*shortopts + ns, &xtra_short, sizeof (xtra_short)); + *longopts = (struct option *) + xrealloc (*longopts, nl * sizeof (struct option) + sizeof (xtra_long)); + memcpy (*longopts + nl, &xtra_long, sizeof (xtra_long)); +} + +static void +gld${EMULATION_NAME}_list_options (FILE * file) +{ + fprintf (file, _(" --code-region={either,lower,upper,none}\n\ + Transform .text* sections to {either,lower,upper,none}.text* sections\n")); + fprintf (file, _(" --data-region={either,lower,upper,none}\n\ + Transform .data*, .rodata* and .bss* sections to\n\ + {either,lower,upper,none}.{bss,data,rodata}* sections\n")); + fprintf (file, _(" --disable-sec-transformation\n\ + Disable transformation of .{text,data,bss,rodata}* sections to\n\ + add the {either,lower,upper,none} prefixes\n")); +} + +static bfd_boolean +gld${EMULATION_NAME}_handle_option (int optc) +{ + switch (optc) + { + case OPTION_CODE_REGION: + if (strcmp (optarg, "upper") == 0) + code_region = REGION_UPPER; + else if (strcmp (optarg, "lower") == 0) + code_region = REGION_LOWER; + else if (strcmp (optarg, "either") == 0) + code_region = REGION_EITHER; + else if (strcmp (optarg, "none") == 0) + code_region = REGION_NONE; + else if (strlen (optarg) == 0) + { + einfo (_("%P: --code-region requires an argument: " + "{upper,lower,either,none}\n")); + return FALSE; + } else - os = lower; + { + einfo (_("%P: error: unrecognized argument to --code-region= option: " + "\"%s\"\n"), optarg); + return FALSE; + } + break; + + case OPTION_DATA_REGION: + if (strcmp (optarg, "upper") == 0) + data_region = REGION_UPPER; + else if (strcmp (optarg, "lower") == 0) + data_region = REGION_LOWER; + else if (strcmp (optarg, "either") == 0) + data_region = REGION_EITHER; + else if (strcmp (optarg, "none") == 0) + data_region = REGION_NONE; + else if (strlen (optarg) == 0) + { + einfo (_("%P: --data-region requires an argument: " + "{upper,lower,either,none}\n")); + return FALSE; + } + else + { + einfo (_("%P: error: unrecognized argument to --data-region= option: " + "\"%s\"\n"), optarg); + return FALSE; + } + break; + + case OPTION_DISABLE_TRANS: + disable_sec_transformation = TRUE; + break; + + default: + return FALSE; } + return TRUE; +} + +static void +eval_upper_either_sections (bfd *abfd ATTRIBUTE_UNUSED, + asection *s, void *data) +{ + const char * base_sec_name; + const char * curr_name; + char * either_name; + int curr_region; + + lang_output_section_statement_type * lower; + lang_output_section_statement_type * upper; + static bfd_size_type *lower_size = 0; + static bfd_size_type *upper_size = 0; + static bfd_size_type lower_size_rom = 0; + static bfd_size_type lower_size_ram = 0; + static bfd_size_type upper_size_rom = 0; + static bfd_size_type upper_size_ram = 0; + + if ((s->flags & SEC_ALLOC) == 0) + return; + if (bfd_link_relocatable (&link_info)) + return; + + base_sec_name = (const char *) data; + curr_name = bfd_section_name (s); + + /* Only concerned with .either input sections in the upper output section. */ + either_name = concat (".either", base_sec_name, NULL); + if (strncmp (curr_name, either_name, strlen (either_name)) != 0 + || strncmp (s->output_section->name, ".upper", 6) != 0) + goto end; + + lower = lang_output_section_find (concat (".lower", base_sec_name, NULL)); + upper = lang_output_section_find (concat (".upper", base_sec_name, NULL)); + + if (upper == NULL || upper->region == NULL) + goto end; + else if (lower == NULL) + lower = lang_output_section_find (base_sec_name); + if (lower == NULL || lower->region == NULL) + goto end; + + if (strcmp (base_sec_name, ".text") == 0 + || strcmp (base_sec_name, ".rodata") == 0) + curr_region = ROM; + else + curr_region = RAM; - lang_add_section (& os->children, s, NULL, os); + if (curr_region == ROM) + { + if (lower_size_rom == 0) + { + lower_size_rom = lower->region->current - lower->region->origin; + upper_size_rom = upper->region->current - upper->region->origin; + } + lower_size = &lower_size_rom; + upper_size = &upper_size_rom; + } + else if (curr_region == RAM) + { + if (lower_size_ram == 0) + { + lower_size_ram = lower->region->current - lower->region->origin; + upper_size_ram = upper->region->current - upper->region->origin; + } + lower_size = &lower_size_ram; + upper_size = &upper_size_ram; + } + + /* Move sections in the upper region that would fit in the lower + region to the lower region. */ + if (*lower_size + s->size < lower->region->length) + { + if (change_output_section (&(upper->children.head), s, lower)) + { + *upper_size -= s->size; + *lower_size += s->size; + } + } end: - free (upper_name); - free (lower_name); - if (buf) - free (buf); - return os; + free (either_name); } -EOF -fi -fragment <flags & SEC_ALLOC) == 0) + return; + if (bfd_link_relocatable (&link_info)) + return; + + base_sec_name = (const char *) data; + curr_name = bfd_section_name (s); + + /* Only concerned with .either input sections in the lower or "default" + output section i.e. not in the upper output section. */ + either_name = concat (".either", base_sec_name, NULL); + if (strncmp (curr_name, either_name, strlen (either_name)) != 0 + || strncmp (s->output_section->name, ".upper", 6) == 0) + return; + + if (strcmp (base_sec_name, ".text") == 0 + || strcmp (base_sec_name, ".rodata") == 0) + curr_region = ROM; + else + curr_region = RAM; + + output_sec = lang_output_section_find (s->output_section->name); + + /* If the output_section doesn't exist, this has already been reported in + place_orphan, so don't need to warn again. */ + if (output_sec == NULL || output_sec->region == NULL) + goto end; + + /* lower and output_sec might be the same, but in some cases an .either + section can end up in base_sec_name if it hasn't been placed by + place_orphan. */ + lower = lang_output_section_find (concat (".lower", base_sec_name, NULL)); + upper = lang_output_section_find (concat (".upper", base_sec_name, NULL)); + if (upper == NULL) + goto end; + + if (curr_region == ROM) + { + if (lower_size_rom == 0) + { + /* Get the size of other items in the lower region that aren't the + sections to be moved around. */ + lower_size_rom + = (output_sec->region->current - output_sec->region->origin) + - scan_children (output_sec->children.head); + if (output_sec != lower && lower != NULL) + lower_size_rom -= scan_children (lower->children.head); + } + lower_size = &lower_size_rom; + } + else if (curr_region == RAM) + { + if (lower_size_ram == 0) + { + lower_size_ram + = (output_sec->region->current - output_sec->region->origin) + - scan_children (output_sec->children.head); + if (output_sec != lower && lower != NULL) + lower_size_ram -= scan_children (lower->children.head); + } + lower_size = &lower_size_ram; + } + /* Move sections that cause the lower region to overflow to the upper region. */ + if (*lower_size + s->size > output_sec->region->length) + change_output_section (&(output_sec->children.head), s, upper); + else + *lower_size += s->size; + end: + free (either_name); +} + +/* This function is similar to lang_relax_sections, but without the size + evaluation code that is always executed after relaxation. */ +static void +intermediate_relax_sections (void) +{ + int i = link_info.relax_pass; + + /* The backend can use it to determine the current pass. */ + link_info.relax_pass = 0; + + while (i--) + { + bfd_boolean relax_again; + + link_info.relax_trip = -1; + do + { + link_info.relax_trip++; + + lang_do_assignments (lang_assigning_phase_enum); + + lang_reset_memory_regions (); + + relax_again = FALSE; + lang_size_sections (&relax_again, FALSE); + } + while (relax_again); + + link_info.relax_pass++; + } +} + +static void +msp430_elf_after_allocation (void) +{ + int relax_count = 0; + unsigned int i; + /* Go over each section twice, once to place either sections that don't fit + in lower into upper, and then again to move any sections in upper that + fit in lower into lower. */ + for (i = 0; i < 8; i++) + { + int placement_stage = (i < 4) ? LOWER_TO_UPPER : UPPER_TO_LOWER; + const char * base_sec_name; + lang_output_section_statement_type * upper; + + switch (i % 4) + { + default: + case 0: + base_sec_name = ".text"; + break; + case 1: + base_sec_name = ".data"; + break; + case 2: + base_sec_name = ".bss"; + break; + case 3: + base_sec_name = ".rodata"; + break; + } + upper = lang_output_section_find (concat (".upper", base_sec_name, NULL)); + if (upper != NULL) + { + /* Can't just use one iteration over the all the sections to make + both lower->upper and upper->lower transformations because the + iterator encounters upper sections before all lower sections have + been examined. */ + bfd *abfd; + + if (placement_stage == LOWER_TO_UPPER) + { + /* Perform relaxation and get the final size of sections + before trying to fit .either sections in the correct + ouput sections. */ + if (relax_count == 0) + { + intermediate_relax_sections (); + relax_count++; + } + for (abfd = link_info.input_bfds; abfd != NULL; + abfd = abfd->link.next) + { + bfd_map_over_sections (abfd, eval_lower_either_sections, + (void *) base_sec_name); + } + } + else if (placement_stage == UPPER_TO_LOWER) + { + /* Relax again before moving upper->lower. */ + if (relax_count == 1) + { + intermediate_relax_sections (); + relax_count++; + } + for (abfd = link_info.input_bfds; abfd != NULL; + abfd = abfd->link.next) + { + bfd_map_over_sections (abfd, eval_upper_either_sections, + (void *) base_sec_name); + } + } + + } + } + gld${EMULATION_NAME}_after_allocation (); +} struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation = { @@ -274,8 +822,10 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation = ${LDEMUL_SYSLIB-syslib_default}, ${LDEMUL_HLL-hll_default}, ${LDEMUL_AFTER_PARSE-after_parse_default}, - ${LDEMUL_AFTER_OPEN-after_open_default}, - ${LDEMUL_AFTER_ALLOCATION-after_allocation_default}, + msp430_elf_after_open, + after_check_relocs_default, + before_place_orphans_default, + msp430_elf_after_allocation, ${LDEMUL_SET_OUTPUT_ARCH-set_output_arch_default}, ${LDEMUL_CHOOSE_TARGET-ldemul_default_target}, ${LDEMUL_BEFORE_ALLOCATION-before_allocation_default}, @@ -288,14 +838,17 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation = ${LDEMUL_PLACE_ORPHAN-gld${EMULATION_NAME}_place_orphan}, ${LDEMUL_SET_SYMBOLS-NULL}, ${LDEMUL_PARSE_ARGS-NULL}, - ${LDEMUL_ADD_OPTIONS-NULL}, - ${LDEMUL_HANDLE_OPTION-NULL}, + gld${EMULATION_NAME}_add_options, + gld${EMULATION_NAME}_handle_option, ${LDEMUL_UNRECOGNIZED_FILE-NULL}, - ${LDEMUL_LIST_OPTIONS-NULL}, + gld${EMULATION_NAME}_list_options, ${LDEMUL_RECOGNIZED_FILE-NULL}, ${LDEMUL_FIND_POTENTIAL_LIBRARIES-NULL}, ${LDEMUL_NEW_VERS_PATTERN-NULL}, - ${LDEMUL_EXTRA_MAP_FILE_TEXT-NULL} + ${LDEMUL_EXTRA_MAP_FILE_TEXT-NULL}, + ${LDEMUL_EMIT_CTF_EARLY-NULL}, + ${LDEMUL_EXAMINE_STRTAB_FOR_CTF-NULL}, + ${LDEMUL_PRINT_SYMBOL-NULL} }; EOF #