This is the mail archive of the binutils@sourceware.cygnus.com 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]

Patch: access to an ELF file's program header table



I've encountered a situation where I believe I need access to an ELF
file's program header table.  I'll below why I think I need it, and
then include a patch which implements this.  I'd be happy to hear
ideas about other ways to accomplish what I need to do, or suggestions
for better ways to extend BFD's interface.  I'd also be happy to hear
that you like the patch as it stands. :)


HP-UX 11.00 running on a PA2.0 in wide mode uses ELF object files,
executables, and shared libraries.  Cygnus has prepared a toolchain
that supports this configuration on a branch; I'm working on merging
the GDB-related changes into the main sources.

In order to debug code in a shared library, GDB needs to know the
offset between the load addresses given in the shared library file and
the addresses at which the dynamic linker actually loaded those parts
of the library.  I think the ABI calls this the shared object file's
`base address', in Chapter 5.  GDB uses the base address to find the
true addresses of things referred to in the debugging info.

Using the extant BFD interface, GDB can only find the load addresses
of *sections* in the shared library file.  However, the HP-UX
interface to the dynamic linker provides only the addresses at which
it loaded the shared library's *segments*.  

As I understand the ABI, there's no necessary relationship between the
segments and the sections.  Segments may contain `holes' that are not
part of any section; a segment may even start with a hole.  So, given
only the information mentioned above, GDB cannot compute the shared
library's base address, because it cannot assume any relationship
between the segments' loaded addresses and the section's addresses as
given in the ELF file.

In fact, HP-UX lays out its shared libraries like this:

$ readelf -l shr1.sl

Elf file is DYN (Shared object file)
Entry point 0x40000000000018b0
There are 4 program headers, starting at offset 40:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000040 0x4000000000001040 0x00000000 0x000e0 0x000e0 R E 0x8
  LOAD           0x000000 0x4000000000001000 0x4000000000001000 0x00ad4 0x00ad4 RWE 0x1000
  LOAD           0x001000 0x8000000000001000 0x8000000000001000 0x000c0 0x000c0 RW  0x1000
  DYNAMIC        0x000120 0x4000000000001120 0x4000000000001120 0x000f0 0x000f0 RW  0x8

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .dynamic .hash .dynsym .dynstr .rela.opd .rela.dlt .rela.plt .text .rodata .PARISC.unwind 
   02     .data .opd .plt .dlt 
   03     .dynamic 


$ objdump -h shr1.sl

shr1.sl:     file format elf64-hppa

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .dynamic      000000f0  4000000000001120  4000000000001120  00000120  2**3
                  CONTENTS, ALLOC, LOAD, DATA
  1 .hash         00000140  4000000000001210  4000000000001210  00000210  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .dynsym       000003d8  4000000000001350  4000000000001350  00000350  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .dynstr       00000091  4000000000001728  4000000000001728  00000728  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .rela.opd     00000048  40000000000017c0  40000000000017c0  000007c0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .rela.dlt     00000090  4000000000001808  4000000000001808  00000808  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .rela.plt     00000018  4000000000001898  4000000000001898  00000898  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .init         00000000  40000000000018b0  40000000000018b0  000010c0  2**0
                  CONTENTS
  8 .text         000001c4  40000000000018b0  40000000000018b0  000008b0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  9 .fini         00000000  4000000000001a74  4000000000001a74  000010c0  2**0
                  CONTENTS
 10 .rodata       00000020  4000000000001a74  4000000000001a74  00000a74  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 11 .PARISC.unwind 00000040  4000000000001a94  4000000000001a94  00000a94  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 12 .data         0000001c  8000000000001000  8000000000001000  00001000  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 13 .opd          00000060  8000000000001020  8000000000001020  00001020  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 14 .plt          00000010  8000000000001080  8000000000001080  00001080  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 15 .dlt          00000030  8000000000001090  8000000000001090  00001090  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 16 .bss          00000000  80000000000010c0  80000000000010c0  000010c0  2**0
                  ALLOC
 17 .debug_aranges 00000030  0000000000000000  0000000000000000  000010c0  2**0
                  CONTENTS, READONLY, DEBUGGING
 18 .debug_pubnames 00000047  0000000000000000  0000000000000000  000010f0  2**0
                  CONTENTS, READONLY, DEBUGGING
 19 .debug_info   00000d30  0000000000000000  0000000000000000  00001137  2**0
                  CONTENTS, READONLY, DEBUGGING
 20 .debug_abbrev 00000116  0000000000000000  0000000000000000  00001e67  2**0
                  CONTENTS, READONLY, DEBUGGING
 21 .debug_line   000000a3  0000000000000000  0000000000000000  00001f7d  2**0
                  CONTENTS, READONLY, DEBUGGING


Note that the text segment starts at file offset zero, while there is
no section which begins at file offset zero.  Since the dynamic
linker's debug interface tells GDB only the address at which the text
segment was loaded, GDB needs to know the address attributed to the
text segment in the ELF file.

Here is a patch which gives BFD clients the ability to read an ELF
file's program header table, in its byte-swapped form:

1999-11-11  Jim Blandy  <jimb@cris.red-bean.com>

	* elf.c (bfd_get_elf_phdrs, bfd_get_elf_phdr_upper_bound): New
	functions.
	* bfd-in2.h (bfd_get_elf_phdrs, bfd_get_elf_phdr_upper_bound): New
	declarations.

Index: bfd/bfd-in2.h
===================================================================
RCS file: /cvs/cvsfiles/devo/bfd/bfd-in2.h,v
retrieving revision 1.347.8.1
diff -c -r1.347.8.1 bfd-in2.h
*** bfd/bfd-in2.h	1999/09/19 00:59:41	1.347.8.1
--- bfd/bfd-in2.h	1999/11/15 17:57:13
***************
*** 3074,3079 ****
--- 3074,3095 ----
  CONST char *
  bfd_format_string PARAMS ((bfd_format format));
  
+ 
+ /* Return an upper bound on the number of bytes required to store a
+    copy of ABFD's program header table entries.  Return -1 if an error
+    occurs; bfd_get_error will return an appropriate code.  */
+ extern long bfd_get_elf_phdr_upper_bound PARAMS ((bfd *abfd));
+ 
+ /* Copy ABFD's program header table entries to *PHDRS.  The entries
+    will be stored as an array of Elf_Internal_Phdr structures, as
+    defined in include/elf/internal.h.  To find out how large the
+    buffer needs to be, call bfd_get_elf_phdr_upper_bound.
+ 
+    Return the number of program header table entries read, or -1 if an
+    error occurs; bfd_get_error will return an appropriate code.  */
+ extern int bfd_get_elf_phdrs PARAMS ((bfd *abfd, void *phdrs));
+ 
+ 
  #ifdef __cplusplus
  }
  #endif
Index: bfd/elf.c
===================================================================
RCS file: /cvs/cvsfiles/devo/bfd/elf.c,v
retrieving revision 1.240.24.3
diff -c -r1.240.24.3 elf.c
*** bfd/elf.c	1999/10/26 20:20:31	1.240.24.3
--- bfd/elf.c	1999/11/15 17:57:15
***************
*** 5344,5346 ****
--- 5344,5394 ----
    return true;
  }
  
+ 
+ 
+ /* Providing external access to the ELF program header table.  */
+ 
+ /* Return an upper bound on the number of bytes required to store a
+    copy of ABFD's program header table entries.  Return -1 if an error
+    occurs; bfd_get_error will return an appropriate code.  */
+ long
+ bfd_get_elf_phdr_upper_bound (abfd)
+      bfd *abfd;
+ {
+   if (abfd->xvec->flavour != bfd_target_elf_flavour)
+     {
+       bfd_set_error (bfd_error_wrong_format);
+       return -1;
+     }
+ 
+   return (elf_elfheader (abfd)->e_phnum
+ 	  * sizeof (Elf_Internal_Phdr));
+ }
+ 
+ 
+ /* Copy ABFD's program header table entries to *PHDRS.  The entries
+    will be stored as an array of Elf_Internal_Phdr structures, as
+    defined in include/elf/internal.h.  To find out how large the
+    buffer needs to be, call bfd_get_elf_phdr_upper_bound.
+ 
+    Return the number of program header table entries read, or -1 if an
+    error occurs; bfd_get_error will return an appropriate code.  */
+ int
+ bfd_get_elf_phdrs (abfd, phdrs)
+      bfd *abfd;
+      void *phdrs;
+ {
+   int num_phdrs;
+ 
+   if (abfd->xvec->flavour != bfd_target_elf_flavour)
+     {
+       bfd_set_error (bfd_error_wrong_format);
+       return -1;
+     }
+ 
+   num_phdrs = elf_elfheader (abfd)->e_phnum;
+   memcpy (phdrs, elf_tdata (abfd)->phdr, 
+ 	  num_phdrs * sizeof (Elf_Internal_Phdr));
+ 
+   return num_phdrs;
+ }




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