| 1 | /* od-avrelf.c -- dump information about an AVR elf object file. |
| 2 | Copyright (C) 2011-2020 Free Software Foundation, Inc. |
| 3 | Written by Senthil Kumar Selvaraj, Atmel. |
| 4 | |
| 5 | This file is part of GNU Binutils. |
| 6 | |
| 7 | This program is free software; you can redistribute it and/or modify |
| 8 | it under the terms of the GNU General Public License as published by |
| 9 | the Free Software Foundation; either version 3, or (at your option) |
| 10 | any later version. |
| 11 | |
| 12 | This program is distributed in the hope that it will be useful, |
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | GNU General Public License for more details. |
| 16 | |
| 17 | You should have received a copy of the GNU General Public License |
| 18 | along with this program; if not, write to the Free Software |
| 19 | Foundation, 51 Franklin Street - Fifth Floor, Boston, |
| 20 | MA 02110-1301, USA. */ |
| 21 | |
| 22 | #include "sysdep.h" |
| 23 | #include <stddef.h> |
| 24 | #include <time.h> |
| 25 | #include <stdint.h> |
| 26 | #include "safe-ctype.h" |
| 27 | #include "bfd.h" |
| 28 | #include "objdump.h" |
| 29 | #include "bucomm.h" |
| 30 | #include "bfdlink.h" |
| 31 | #include "bfd.h" |
| 32 | #include "elf/external.h" |
| 33 | #include "elf/internal.h" |
| 34 | #include "elf32-avr.h" |
| 35 | |
| 36 | /* Index of the options in the options[] array. */ |
| 37 | #define OPT_MEMUSAGE 0 |
| 38 | #define OPT_AVRPROP 1 |
| 39 | |
| 40 | /* List of actions. */ |
| 41 | static struct objdump_private_option options[] = |
| 42 | { |
| 43 | { "mem-usage", 0 }, |
| 44 | { "avr-prop", 0}, |
| 45 | { NULL, 0 } |
| 46 | }; |
| 47 | |
| 48 | /* Display help. */ |
| 49 | |
| 50 | static void |
| 51 | elf32_avr_help (FILE *stream) |
| 52 | { |
| 53 | fprintf (stream, _("\ |
| 54 | For AVR ELF files:\n\ |
| 55 | mem-usage Display memory usage\n\ |
| 56 | avr-prop Display contents of .avr.prop section\n\ |
| 57 | ")); |
| 58 | } |
| 59 | |
| 60 | typedef struct tagDeviceInfo |
| 61 | { |
| 62 | uint32_t flash_start; |
| 63 | uint32_t flash_size; |
| 64 | uint32_t ram_start; |
| 65 | uint32_t ram_size; |
| 66 | uint32_t eeprom_start; |
| 67 | uint32_t eeprom_size; |
| 68 | char * name; |
| 69 | } deviceinfo; |
| 70 | |
| 71 | |
| 72 | /* Return TRUE if ABFD is handled. */ |
| 73 | |
| 74 | static int |
| 75 | elf32_avr_filter (bfd *abfd) |
| 76 | { |
| 77 | return bfd_get_flavour (abfd) == bfd_target_elf_flavour; |
| 78 | } |
| 79 | |
| 80 | static char* |
| 81 | elf32_avr_get_note_section_contents (bfd *abfd, bfd_size_type *size) |
| 82 | { |
| 83 | asection *section; |
| 84 | |
| 85 | if ((section = bfd_get_section_by_name (abfd, ".note.gnu.avr.deviceinfo")) == NULL) |
| 86 | return NULL; |
| 87 | |
| 88 | *size = bfd_section_size (section); |
| 89 | char *contents = (char *) xmalloc (*size); |
| 90 | bfd_get_section_contents (abfd, section, contents, 0, *size); |
| 91 | |
| 92 | return contents; |
| 93 | } |
| 94 | |
| 95 | static char* elf32_avr_get_note_desc (bfd *abfd, char *contents, |
| 96 | bfd_size_type size) |
| 97 | { |
| 98 | Elf_External_Note *xnp = (Elf_External_Note *) contents; |
| 99 | Elf_Internal_Note in; |
| 100 | |
| 101 | if (offsetof (Elf_External_Note, name) > size) |
| 102 | return NULL; |
| 103 | |
| 104 | in.type = bfd_get_32 (abfd, xnp->type); |
| 105 | in.namesz = bfd_get_32 (abfd, xnp->namesz); |
| 106 | in.namedata = xnp->name; |
| 107 | if (in.namesz > contents - in.namedata + size) |
| 108 | return NULL; |
| 109 | |
| 110 | in.descsz = bfd_get_32 (abfd, xnp->descsz); |
| 111 | in.descdata = in.namedata + align_power (in.namesz, 2); |
| 112 | if (in.descsz != 0 |
| 113 | && (in.descdata >= contents + size |
| 114 | || in.descsz > contents - in.descdata + size)) |
| 115 | return NULL; |
| 116 | |
| 117 | if (strcmp (in.namedata, "AVR") != 0) |
| 118 | return NULL; |
| 119 | |
| 120 | return in.descdata; |
| 121 | } |
| 122 | |
| 123 | static void |
| 124 | elf32_avr_get_device_info (bfd *abfd, char *description, |
| 125 | deviceinfo *device) |
| 126 | { |
| 127 | if (description == NULL) |
| 128 | return; |
| 129 | |
| 130 | const bfd_size_type memory_sizes = 6; |
| 131 | |
| 132 | memcpy (device, description, memory_sizes * sizeof(uint32_t)); |
| 133 | device->name = NULL; |
| 134 | |
| 135 | uint32_t *stroffset_table = ((uint32_t *) description) + memory_sizes; |
| 136 | bfd_size_type stroffset_table_size = bfd_get_32 (abfd, stroffset_table); |
| 137 | char *str_table = ((char *) stroffset_table) + stroffset_table_size; |
| 138 | |
| 139 | /* If the only content is the size itself, there's nothing in the table */ |
| 140 | if (stroffset_table_size == 4) |
| 141 | return; |
| 142 | |
| 143 | /* First entry is the device name index. */ |
| 144 | uint32_t device_name_index = bfd_get_32 (abfd, stroffset_table + 1); |
| 145 | |
| 146 | device->name = str_table + device_name_index; |
| 147 | } |
| 148 | |
| 149 | static void |
| 150 | elf32_avr_get_memory_usage (bfd *abfd, |
| 151 | bfd_size_type *text_usage, |
| 152 | bfd_size_type *data_usage, |
| 153 | bfd_size_type *eeprom_usage) |
| 154 | { |
| 155 | |
| 156 | bfd_size_type avr_datasize = 0; |
| 157 | bfd_size_type avr_textsize = 0; |
| 158 | bfd_size_type avr_bsssize = 0; |
| 159 | bfd_size_type bootloadersize = 0; |
| 160 | bfd_size_type noinitsize = 0; |
| 161 | bfd_size_type eepromsize = 0; |
| 162 | asection *section; |
| 163 | |
| 164 | if ((section = bfd_get_section_by_name (abfd, ".data")) != NULL) |
| 165 | avr_datasize = bfd_section_size (section); |
| 166 | if ((section = bfd_get_section_by_name (abfd, ".text")) != NULL) |
| 167 | avr_textsize = bfd_section_size (section); |
| 168 | if ((section = bfd_get_section_by_name (abfd, ".bss")) != NULL) |
| 169 | avr_bsssize = bfd_section_size (section); |
| 170 | if ((section = bfd_get_section_by_name (abfd, ".bootloader")) != NULL) |
| 171 | bootloadersize = bfd_section_size (section); |
| 172 | if ((section = bfd_get_section_by_name (abfd, ".noinit")) != NULL) |
| 173 | noinitsize = bfd_section_size (section); |
| 174 | if ((section = bfd_get_section_by_name (abfd, ".eeprom")) != NULL) |
| 175 | eepromsize = bfd_section_size (section); |
| 176 | |
| 177 | *text_usage = avr_textsize + avr_datasize + bootloadersize; |
| 178 | *data_usage = avr_datasize + avr_bsssize + noinitsize; |
| 179 | *eeprom_usage = eepromsize; |
| 180 | } |
| 181 | |
| 182 | static void |
| 183 | elf32_avr_dump_mem_usage (bfd *abfd) |
| 184 | { |
| 185 | char *description = NULL; |
| 186 | bfd_size_type note_section_size = 0; |
| 187 | |
| 188 | deviceinfo device = { 0, 0, 0, 0, 0, 0, NULL }; |
| 189 | device.name = "Unknown"; |
| 190 | |
| 191 | bfd_size_type data_usage = 0; |
| 192 | bfd_size_type text_usage = 0; |
| 193 | bfd_size_type eeprom_usage = 0; |
| 194 | |
| 195 | char *contents = elf32_avr_get_note_section_contents (abfd, |
| 196 | ¬e_section_size); |
| 197 | |
| 198 | if (contents != NULL) |
| 199 | { |
| 200 | description = elf32_avr_get_note_desc (abfd, contents, note_section_size); |
| 201 | elf32_avr_get_device_info (abfd, description, &device); |
| 202 | } |
| 203 | |
| 204 | elf32_avr_get_memory_usage (abfd, &text_usage, &data_usage, |
| 205 | &eeprom_usage); |
| 206 | |
| 207 | printf ("AVR Memory Usage\n" |
| 208 | "----------------\n" |
| 209 | "Device: %s\n\n", device.name); |
| 210 | |
| 211 | /* Text size */ |
| 212 | printf ("Program:%8ld bytes", text_usage); |
| 213 | if (device.flash_size > 0) |
| 214 | printf (" (%2.1f%% Full)", ((float) text_usage / device.flash_size) * 100); |
| 215 | |
| 216 | printf ("\n(.text + .data + .bootloader)\n\n"); |
| 217 | |
| 218 | /* Data size */ |
| 219 | printf ("Data: %8ld bytes", data_usage); |
| 220 | if (device.ram_size > 0) |
| 221 | printf (" (%2.1f%% Full)", ((float) data_usage / device.ram_size) * 100); |
| 222 | |
| 223 | printf ("\n(.data + .bss + .noinit)\n\n"); |
| 224 | |
| 225 | /* EEPROM size */ |
| 226 | if (eeprom_usage > 0) |
| 227 | { |
| 228 | printf ("EEPROM: %8ld bytes", eeprom_usage); |
| 229 | if (device.eeprom_size > 0) |
| 230 | printf (" (%2.1f%% Full)", ((float) eeprom_usage / device.eeprom_size) * 100); |
| 231 | |
| 232 | printf ("\n(.eeprom)\n\n"); |
| 233 | } |
| 234 | |
| 235 | if (contents != NULL) |
| 236 | free (contents); |
| 237 | |
| 238 | } |
| 239 | |
| 240 | static void |
| 241 | elf32_avr_dump_avr_prop (bfd *abfd) |
| 242 | { |
| 243 | struct avr_property_record_list *r_list; |
| 244 | unsigned int i; |
| 245 | |
| 246 | r_list = avr_elf32_load_property_records (abfd); |
| 247 | if (r_list == NULL) |
| 248 | return; |
| 249 | |
| 250 | printf ("\nContents of `%s' section:\n\n", r_list->section->name); |
| 251 | |
| 252 | printf (" Version: %d\n", r_list->version); |
| 253 | printf (" Flags: %#x\n\n", r_list->flags); |
| 254 | |
| 255 | for (i = 0; i < r_list->record_count; ++i) |
| 256 | { |
| 257 | printf (" %d %s @ %s + %#08lx (%#08lx)\n", |
| 258 | i, |
| 259 | avr_elf32_property_record_name (&r_list->records [i]), |
| 260 | r_list->records [i].section->name, |
| 261 | r_list->records [i].offset, |
| 262 | (bfd_section_vma (r_list->records [i].section) |
| 263 | + r_list->records [i].offset)); |
| 264 | switch (r_list->records [i].type) |
| 265 | { |
| 266 | case RECORD_ORG: |
| 267 | /* Nothing else to print. */ |
| 268 | break; |
| 269 | case RECORD_ORG_AND_FILL: |
| 270 | printf (" Fill: %#08lx\n", |
| 271 | r_list->records [i].data.org.fill); |
| 272 | break; |
| 273 | case RECORD_ALIGN: |
| 274 | printf (" Align: %#08lx\n", |
| 275 | r_list->records [i].data.align.bytes); |
| 276 | break; |
| 277 | case RECORD_ALIGN_AND_FILL: |
| 278 | printf (" Align: %#08lx, Fill: %#08lx\n", |
| 279 | r_list->records [i].data.align.bytes, |
| 280 | r_list->records [i].data.align.fill); |
| 281 | break; |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | free (r_list); |
| 286 | } |
| 287 | |
| 288 | static void |
| 289 | elf32_avr_dump (bfd *abfd) |
| 290 | { |
| 291 | if (options[OPT_MEMUSAGE].selected) |
| 292 | elf32_avr_dump_mem_usage (abfd); |
| 293 | if (options[OPT_AVRPROP].selected) |
| 294 | elf32_avr_dump_avr_prop (abfd); |
| 295 | } |
| 296 | |
| 297 | const struct objdump_private_desc objdump_private_desc_elf32_avr = |
| 298 | { |
| 299 | elf32_avr_help, |
| 300 | elf32_avr_filter, |
| 301 | elf32_avr_dump, |
| 302 | options |
| 303 | }; |