This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 2/3] avr/objdump: Support dumping .avr.prop section.


This includes tests that cover patch 1 and 2 of this series.

Feedback welcome,

thanks,
Andrew

---
Add support to objdump for dumping the .avr.prop section in a structured
way.

binutils/ChangeLog:

	* od-elf32_avr.c: Add elf32-avr.h include.
	(OPT_AVRPROP): Define.
	(options[]): Add 'avr-prop' entry.
	(elf32_avr_help): Add avr-prop help text.
	(elf32_avr_dump_avr_prop): New function.
	(elf32_avr_dump): Add check for avr-prop.

bfd/ChangeLog:

	* elf32-avr.h (struct avr_property_header): New strucure.
	(avr_elf32_load_property_records): Declare.
	(avr_elf32_property_record_name): Declare.
	* elf32-avr.c: Add stdint.h include.
	(retrieve_local_syms): New function.
	(get_elf_r_symndx_section): New function.
	(get_elf_r_symndx_offset): New function.
	(internal_reloc_compare): New function.
	(struct avr_find_section_data): New structure.
	(avr_is_section_for_address): New function.
	(avr_find_section_for_address): New function.
	(avr_elf32_load_records_from_section): New function.
	(avr_elf32_load_property_records): New function.
	(avr_elf32_property_record_name): New function.

gas/testsuite/ChangeLog:

	* gas/avr/avr-prop-1.d: New file.
	* gas/avr/avr-prop-1.s: New file.
---
 bfd/elf32-avr.c                    | 440 +++++++++++++++++++++++++++++++++++++
 bfd/elf32-avr.h                    |  32 +++
 binutils/od-elf32_avr.c            |  54 +++++
 gas/testsuite/gas/avr/avr-prop-1.d |  26 +++
 gas/testsuite/gas/avr/avr-prop-1.s |  29 +++
 5 files changed, 581 insertions(+)
 create mode 100644 gas/testsuite/gas/avr/avr-prop-1.d
 create mode 100644 gas/testsuite/gas/avr/avr-prop-1.s

diff --git a/bfd/elf32-avr.c b/bfd/elf32-avr.c
index adcf1a3..af5fd8a 100644
--- a/bfd/elf32-avr.c
+++ b/bfd/elf32-avr.c
@@ -26,6 +26,8 @@
 #include "elf/avr.h"
 #include "elf32-avr.h"
 
+#include <stdint.h>
+
 /* Enable debugging printout at stdout with this variable.  */
 static bfd_boolean debug_relax = FALSE;
 
@@ -1935,6 +1937,118 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
   return TRUE;
 }
 
+static Elf_Internal_Sym *
+retrieve_local_syms (bfd *input_bfd)
+{
+  Elf_Internal_Shdr *symtab_hdr;
+  Elf_Internal_Sym *isymbuf;
+  size_t locsymcount;
+
+  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  locsymcount = symtab_hdr->sh_info;
+
+  isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+  if (isymbuf == NULL && locsymcount != 0)
+    isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0,
+				    NULL, NULL, NULL);
+
+  /* Save the symbols for this input file so they won't be read again.  */
+  if (isymbuf && isymbuf != (Elf_Internal_Sym *) symtab_hdr->contents)
+    symtab_hdr->contents = (unsigned char *) isymbuf;
+
+  return isymbuf;
+}
+
+/* Get the input section for a given symbol index.
+   If the symbol is:
+   . a section symbol, return the section;
+   . a common symbol, return the common section;
+   . an undefined symbol, return the undefined section;
+   . an indirect symbol, follow the links;
+   . an absolute value, return the absolute section.  */
+
+static asection *
+get_elf_r_symndx_section (bfd *abfd, unsigned long r_symndx)
+{
+  Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  asection *target_sec = NULL;
+  if (r_symndx < symtab_hdr->sh_info)
+    {
+      Elf_Internal_Sym *isymbuf;
+      unsigned int section_index;
+
+      isymbuf = retrieve_local_syms (abfd);
+      section_index = isymbuf[r_symndx].st_shndx;
+
+      if (section_index == SHN_UNDEF)
+	target_sec = bfd_und_section_ptr;
+      else if (section_index == SHN_ABS)
+	target_sec = bfd_abs_section_ptr;
+      else if (section_index == SHN_COMMON)
+	target_sec = bfd_com_section_ptr;
+      else
+	target_sec = bfd_section_from_elf_index (abfd, section_index);
+    }
+  else
+    {
+      unsigned long indx = r_symndx - symtab_hdr->sh_info;
+      struct elf_link_hash_entry *h = elf_sym_hashes (abfd)[indx];
+
+      while (h->root.type == bfd_link_hash_indirect
+             || h->root.type == bfd_link_hash_warning)
+        h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+      switch (h->root.type)
+	{
+	case bfd_link_hash_defined:
+	case  bfd_link_hash_defweak:
+	  target_sec = h->root.u.def.section;
+	  break;
+	case bfd_link_hash_common:
+	  target_sec = bfd_com_section_ptr;
+	  break;
+	case bfd_link_hash_undefined:
+	case bfd_link_hash_undefweak:
+	  target_sec = bfd_und_section_ptr;
+	  break;
+	default: /* New indirect warning.  */
+	  target_sec = bfd_und_section_ptr;
+	  break;
+	}
+    }
+  return target_sec;
+}
+
+/* Get the section-relative offset for a symbol number.  */
+
+static bfd_vma
+get_elf_r_symndx_offset (bfd *abfd, unsigned long r_symndx)
+{
+  Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  bfd_vma offset = 0;
+
+  if (r_symndx < symtab_hdr->sh_info)
+    {
+      Elf_Internal_Sym *isymbuf;
+      isymbuf = retrieve_local_syms (abfd);
+      offset = isymbuf[r_symndx].st_value;
+    }
+  else
+    {
+      unsigned long indx = r_symndx - symtab_hdr->sh_info;
+      struct elf_link_hash_entry *h =
+	elf_sym_hashes (abfd)[indx];
+
+      while (h->root.type == bfd_link_hash_indirect
+             || h->root.type == bfd_link_hash_warning)
+	h = (struct elf_link_hash_entry *) h->root.u.i.link;
+      if (h->root.type == bfd_link_hash_defined
+          || h->root.type == bfd_link_hash_defweak)
+	offset = h->root.u.def.value;
+    }
+  return offset;
+}
+
 /* This function handles relaxing for the avr.
    Many important relaxing opportunities within functions are already
    realized by the compiler itself.
@@ -3347,6 +3461,332 @@ elf32_avr_build_stubs (struct bfd_link_info *info)
   return TRUE;
 }
 
+/* Callback used by QSORT to order relocations AP and BP.  */
+
+static int
+internal_reloc_compare (const void *ap, const void *bp)
+{
+  const Elf_Internal_Rela *a = (const Elf_Internal_Rela *) ap;
+  const Elf_Internal_Rela *b = (const Elf_Internal_Rela *) bp;
+
+  if (a->r_offset != b->r_offset)
+    return (a->r_offset - b->r_offset);
+
+  /* We don't need to sort on these criteria for correctness,
+     but enforcing a more strict ordering prevents unstable qsort
+     from behaving differently with different implementations.
+     Without the code below we get correct but different results
+     on Solaris 2.7 and 2.8.  We would like to always produce the
+     same results no matter the host.  */
+
+  if (a->r_info != b->r_info)
+    return (a->r_info - b->r_info);
+
+  return (a->r_addend - b->r_addend);
+}
+
+/* Return true if ADDRESS is within the vma range of SECTION from ABFD.  */
+
+static bfd_boolean
+avr_is_section_for_address (bfd *abfd, asection *section, bfd_vma address)
+{
+  bfd_vma vma;
+  bfd_size_type size;
+
+  vma = bfd_get_section_vma (abfd, section);
+  if (address < vma)
+    return FALSE;
+
+  size = section->size;
+  if (address >= vma + size)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Data structure used by AVR_FIND_SECTION_FOR_ADDRESS.  */
+
+struct avr_find_section_data
+{
+  /* The address we're looking for.  */
+  bfd_vma address;
+
+  /* The section we've found.  */
+  asection *section;
+};
+
+/* Helper function to locate the section holding a certain virtual memory
+   address.  This is called via bfd_map_over_sections.  The DATA is an
+   instance of STRUCT AVR_FIND_SECTION_DATA, the address field of which
+   has been set to the address to search for, and the section field has
+   been set to NULL.  If SECTION from ABFD contains ADDRESS then the
+   section field in DATA will be set to SECTION.  As an optimisation, if
+   the section field is already non-null then this function does not
+   perform any checks, and just returns.  */
+
+static void
+avr_find_section_for_address (bfd *abfd,
+                              asection *section, void *data)
+{
+  struct avr_find_section_data *fs_data
+    = (struct avr_find_section_data *) data;
+
+  /* Return if already found.  */
+  if (fs_data->section != NULL)
+    return;
+
+  /* If this section isn't part of the addressable code content, skip it.  */
+  if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0
+      && (bfd_get_section_flags (abfd, section) & SEC_CODE) == 0)
+    return;
+
+  if (avr_is_section_for_address (abfd, section, fs_data->address))
+    fs_data->section = section;
+}
+
+/* Load all of the property records from SEC, a section from ABFD.  Return
+   a STRUCT AVR_PROPERTY_RECORD_LIST containing all the records.  The
+   memory for the returned structure, and all of the records pointed too by
+   the structure are allocated with a single call to malloc, so, only the
+   pointer returned needs to be free'd.  */
+
+static struct avr_property_record_list *
+avr_elf32_load_records_from_section (bfd *abfd, asection *sec)
+{
+  char *contents = NULL, *ptr;
+  bfd_size_type size, mem_size;
+  bfd_byte version, flags;
+  uint16_t record_count, i;
+  struct avr_property_record_list *r_list = NULL;
+  Elf_Internal_Rela *internal_relocs = NULL, *rel, *rel_end;
+  struct avr_find_section_data fs_data;
+
+  fs_data.section = NULL;
+
+  size = bfd_get_section_size (sec);
+  contents = bfd_malloc (size);
+  bfd_get_section_contents (abfd, sec, contents, 0, size);
+  ptr = contents;
+
+  /* Load the relocations for the '.avr.prop' section if there are any, and
+     sort them.  */
+  internal_relocs = (_bfd_elf_link_read_relocs
+                     (abfd, sec, NULL, NULL, FALSE));
+  if (internal_relocs)
+    qsort (internal_relocs, sec->reloc_count,
+           sizeof (Elf_Internal_Rela), internal_reloc_compare);
+
+  /* There is a header at the start of the property record section SEC, the
+     format of this header is:
+       uint8_t  : version number
+       uint8_t  : flags
+       uint16_t : record counter
+  */
+
+  /* Check we have at least got a headers worth of bytes.  */
+  if (size < AVR_PROPERTY_SECTION_HEADER_SIZE)
+    goto load_failed;
+
+  version = *((bfd_byte *) ptr);
+  ptr++;
+  flags = *((bfd_byte *) ptr);
+  ptr++;
+  record_count = *((uint16_t *) ptr);
+  ptr+=2;
+  BFD_ASSERT (ptr - contents == AVR_PROPERTY_SECTION_HEADER_SIZE);
+
+  /* Now allocate space for the list structure, and all of the list
+     elements in a single block.  */
+  mem_size = sizeof (struct avr_property_record_list)
+    + sizeof (struct avr_property_record) * record_count;
+  r_list = bfd_malloc (mem_size);
+  if (r_list == NULL)
+    goto load_failed;
+
+  r_list->version = version;
+  r_list->flags = flags;
+  r_list->section = sec;
+  r_list->record_count = record_count;
+  r_list->records = (struct avr_property_record *) (&r_list [1]);
+  size -= AVR_PROPERTY_SECTION_HEADER_SIZE;
+
+  /* Check that we understand the version number.  There is only one
+     version number right now, anything else is an error.  */
+  if (r_list->version != AVR_PROPERTY_RECORDS_VERSION)
+    goto load_failed;
+
+  rel = internal_relocs;
+  rel_end = rel + sec->reloc_count;
+  for (i = 0; i < record_count; ++i)
+    {
+      bfd_vma address;
+
+      /* Each entry is a 32-bit address, followed by a single byte type.
+         After that is the type specific data.  We must take care to
+         ensure that we don't read beyond the end of the section data.  */
+      if (size < 5)
+        goto load_failed;
+
+      r_list->records [i].section = NULL;
+      r_list->records [i].offset = 0;
+
+      if (rel)
+        {
+          /* The offset of the address within the .avr.prop section.  */
+          size_t offset = ptr - contents;
+
+          while (rel < rel_end && rel->r_offset < offset)
+            ++rel;
+
+          if (rel == rel_end)
+            rel = NULL;
+          else if (rel->r_offset == offset)
+            {
+              /* Find section and section offset.  */
+              unsigned long r_symndx;
+
+              asection * rel_sec;
+              bfd_vma sec_offset;
+
+              r_symndx = ELF32_R_SYM (rel->r_info);
+              rel_sec = get_elf_r_symndx_section (abfd, r_symndx);
+              sec_offset = get_elf_r_symndx_offset (abfd, r_symndx)
+                + rel->r_addend;
+
+              r_list->records [i].section = rel_sec;
+              r_list->records [i].offset = sec_offset;
+            }
+        }
+
+      address = *((uint32_t *) ptr);
+      ptr += 4;
+      size -= 4;
+
+      if (r_list->records [i].section == NULL)
+        {
+          /* Try to find section and offset from address.  */
+          if (fs_data.section != NULL
+              && !avr_is_section_for_address (abfd, fs_data.section,
+                                              address))
+            fs_data.section = NULL;
+
+          if (fs_data.section == NULL)
+            {
+              fs_data.address = address;
+              bfd_map_over_sections (abfd, avr_find_section_for_address,
+                                     &fs_data);
+            }
+
+          if (fs_data.section == NULL)
+            {
+              fprintf (stderr, "Failed to find matching section.\n");
+              goto load_failed;
+            }
+
+          r_list->records [i].section = fs_data.section;
+          r_list->records [i].offset
+            = address - bfd_get_section_vma (abfd, fs_data.section);
+        }
+
+      r_list->records [i].type = *((bfd_byte *) ptr);
+      ptr += 1;
+      size -= 1;
+
+      switch (r_list->records [i].type)
+        {
+        case RECORD_ORG:
+          /* Nothing else to load.  */
+          break;
+        case RECORD_ORG_AND_FILL:
+          /* Just a 4-byte fill to load.  */
+          if (size < 4)
+            goto load_failed;
+          r_list->records [i].data.org.fill = *((uint32_t *) ptr);
+          ptr += 4;
+          size -= 4;
+          break;
+        case RECORD_ALIGN:
+          /* Just a 4-byte alignment to load.  */
+          if (size < 4)
+            goto load_failed;
+          r_list->records [i].data.align.bytes = *((uint32_t *) ptr);
+          ptr += 4;
+          size -= 4;
+          /* Just initialise PRECEDING_DELETED field, this field is
+             used during linker relaxation.  */
+          r_list->records [i].data.align.preceding_deleted = 0;
+          break;
+        case RECORD_ALIGN_AND_FILL:
+          /* A 4-byte alignment, and a 4-byte fill to load.  */
+          if (size < 8)
+            goto load_failed;
+          r_list->records [i].data.align.bytes = *((uint32_t *) ptr);
+          ptr += 4;
+          r_list->records [i].data.align.fill = *((uint32_t *) ptr);
+          ptr += 4;
+          size -= 8;
+          /* Just initialise PRECEDING_DELETED field, this field is
+             used during linker relaxation.  */
+          r_list->records [i].data.align.preceding_deleted = 0;
+          break;
+        default:
+          goto load_failed;
+        }
+    }
+
+  free (contents);
+  free (internal_relocs);
+  return r_list;
+
+ load_failed:
+  free (internal_relocs);
+  free (contents);
+  free (r_list);
+  return NULL;
+}
+
+/* Load all of the property records from ABFD.  See
+   AVR_ELF32_LOAD_RECORDS_FROM_SECTION for details of the return value.  */
+
+struct avr_property_record_list *
+avr_elf32_load_property_records (bfd *abfd)
+{
+  asection *sec;
+
+  /* Find the '.avr.prop' section and load the contents into memory.  */
+  sec = bfd_get_section_by_name (abfd, AVR_PROPERTY_RECORD_SECTION_NAME);
+  if (sec == NULL)
+    return NULL;
+  return avr_elf32_load_records_from_section (abfd, sec);
+}
+
+const char *
+avr_elf32_property_record_name (struct avr_property_record *rec)
+{
+  const char *str;
+
+  switch (rec->type)
+    {
+    case RECORD_ORG:
+      str = "ORG";
+      break;
+    case RECORD_ORG_AND_FILL:
+      str = "ORG+FILL";
+      break;
+    case RECORD_ALIGN:
+      str = "ALIGN";
+      break;
+    case RECORD_ALIGN_AND_FILL:
+      str = "ALIGN+FILL";
+      break;
+    default:
+      str = "unknown";
+    }
+
+  return str;
+}
+
+
 #define ELF_ARCH		bfd_arch_avr
 #define ELF_TARGET_ID		AVR_ELF_DATA
 #define ELF_MACHINE_CODE	EM_AVR
diff --git a/bfd/elf32-avr.h b/bfd/elf32-avr.h
index 688b706..0ddb562 100644
--- a/bfd/elf32-avr.h
+++ b/bfd/elf32-avr.h
@@ -88,3 +88,35 @@ struct avr_property_record
     } align;
   } data;
 };
+
+struct avr_property_record_list
+{
+  /* The version number tells us the structure of the property record data
+     within the section.  See AVR_PROPERTY_RECORDS_VERSION.  */
+  bfd_byte version;
+
+  /* The flags field is currently unused.  This should be set to 0.  */
+  bfd_byte flags;
+
+  /* The number of property records.  This is stored as a 2-byte value in
+     the section contents.  */
+  unsigned long record_count;
+
+  /* The section from which the property records were loaded.  This is the
+     actual section containing the records, not the section(s) to which the
+     records apply.  */
+  asection *section;
+
+  /* The actual property records.  */
+  struct avr_property_record *records;
+};
+
+/* Load the property records from ABFD, return NULL if there are non
+   found, otherwise return pointer to dynamically allocated memory.  The
+   memory for the header and all of the records are allocated in a single
+   block, as such only the header needs to be freed.  */
+
+extern struct avr_property_record_list *avr_elf32_load_property_records (bfd *abfd);
+
+/* Return a string that is the name of the property record pointed to by REC.  */
+extern const char *avr_elf32_property_record_name (struct avr_property_record *rec);
diff --git a/binutils/od-elf32_avr.c b/binutils/od-elf32_avr.c
index 5e828fb..5635964 100644
--- a/binutils/od-elf32_avr.c
+++ b/binutils/od-elf32_avr.c
@@ -31,14 +31,17 @@
 #include "bfd.h"
 #include "elf/external.h"
 #include "elf/internal.h"
+#include "elf32-avr.h"
 
 /* Index of the options in the options[] array.  */
 #define OPT_MEMUSAGE 0
+#define OPT_AVRPROP 1
 
 /* List of actions.  */
 static struct objdump_private_option options[] =
   {
     { "mem-usage", 0 },
+    { "avr-prop",  0},
     { NULL, 0 }
   };
 
@@ -50,6 +53,7 @@ elf32_avr_help (FILE *stream)
   fprintf (stream, _("\
 For AVR ELF files:\n\
   mem-usage   Display memory usage\n\
+  avr-prop    Display contents of .avr.prop section\n\
 "));
 }
 
@@ -234,10 +238,60 @@ elf32_avr_dump_mem_usage (bfd *abfd)
 }
 
 static void
+elf32_avr_dump_avr_prop (bfd *abfd)
+{
+  struct avr_property_record_list *r_list;
+  unsigned int i;
+
+  r_list = avr_elf32_load_property_records (abfd);
+  if (r_list == NULL)
+    return;
+
+  printf ("\nContents of `%s' section:\n\n", r_list->section->name);
+
+  printf ("  Version: %d\n", r_list->version);
+  printf ("  Flags:   %#x\n\n", r_list->flags);
+
+  for (i = 0; i < r_list->record_count; ++i)
+    {
+      printf ("   %d %s @ %s + %#08lx (%#08lx)\n",
+              i,
+              avr_elf32_property_record_name (&r_list->records [i]),
+              r_list->records [i].section->name,
+              r_list->records [i].offset,
+              (bfd_get_section_vma (abfd, r_list->records [i].section)
+               + r_list->records [i].offset));
+      switch (r_list->records [i].type)
+        {
+        case RECORD_ORG:
+          /* Nothing else to print.  */
+          break;
+        case RECORD_ORG_AND_FILL:
+          printf ("     Fill: %#08lx\n",
+                  r_list->records [i].data.org.fill);
+          break;
+        case RECORD_ALIGN:
+          printf ("    Align: %#08lx\n",
+                  r_list->records [i].data.align.bytes);
+          break;
+        case RECORD_ALIGN_AND_FILL:
+          printf ("    Align: %#08lx, Fill: %#08lx\n",
+                  r_list->records [i].data.align.bytes,
+                  r_list->records [i].data.org.fill);
+          break;
+        }
+    }
+
+  free (r_list);
+}
+
+static void
 elf32_avr_dump (bfd *abfd)
 {
   if (options[OPT_MEMUSAGE].selected)
     elf32_avr_dump_mem_usage (abfd);
+  if (options[OPT_AVRPROP].selected)
+    elf32_avr_dump_avr_prop (abfd);
 }
 
 const struct objdump_private_desc objdump_private_desc_elf32_avr =
diff --git a/gas/testsuite/gas/avr/avr-prop-1.d b/gas/testsuite/gas/avr/avr-prop-1.d
new file mode 100644
index 0000000..b140ae6
--- /dev/null
+++ b/gas/testsuite/gas/avr/avr-prop-1.d
@@ -0,0 +1,26 @@
+#name: AVR '.avr.prop' test 1
+#as: -mmcu=avrxmega2 -mlink-relax
+#objdump: -P avr-prop
+#source: avr-prop-1.s
+#target: avr-*-*
+
+.*:     file format elf32-avr
+
+Contents of `\.avr\.prop' section:
+
+  Version: 1
+  Flags:   0
+
+   0 ORG @ \.text\.1 \+ 0x000020 \(0x000020\)
+   1 ORG @ \.text\.1 \+ 0x000044 \(0x000044\)
+   2 ORG @ \.text\.2 \+ 0x000020 \(0x000020\)
+   3 ALIGN @ \.text\.2 \+ 0x000020 \(0x000020\)
+    Align: 0x000004
+   4 ALIGN @ \.text\.2 \+ 0x000030 \(0x000030\)
+    Align: 0x000004
+   5 ORG @ \.text\.2 \+ 0x000200 \(0x000200\)
+   6 ALIGN @ \.text\.2 \+ 0x000200 \(0x000200\)
+    Align: 0x000004
+   7 ALIGN @ \.text\.3 \+ 0x000100 \(0x000100\)
+    Align: 0x000008
+
diff --git a/gas/testsuite/gas/avr/avr-prop-1.s b/gas/testsuite/gas/avr/avr-prop-1.s
new file mode 100644
index 0000000..6e50cf1
--- /dev/null
+++ b/gas/testsuite/gas/avr/avr-prop-1.s
@@ -0,0 +1,29 @@
+        .section ".text.1", "ax"
+        .global _start
+_start:
+        .org 0x20
+        nop
+        .org 0x44
+        nop
+
+
+        .section ".text.2", "ax"
+        .global test2
+text2:
+        .org 0x20
+        nop
+        .align 4
+        nop
+        .align 4
+        nop
+        .org 0x200
+        nop
+
+        .section ".text.3", "ax"
+        .global test3
+text3:
+        .org 0x0
+        nop
+        nop
+        .align 8
+        nop
-- 
1.9.3


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]