This is the mail archive of the
gdb@sources.redhat.com
mailing list for the GDB project.
Re: relocation of shared libs not based at 0
- From: Kevin Buettner <kevinb at redhat dot com>
- To: "Kris Warkentin" <kewarken at qnx dot com>, <gdb at sources dot redhat dot com>
- Date: Tue, 17 Dec 2002 17:28:02 -0700
- Subject: Re: relocation of shared libs not based at 0
- References: <032c01c2a60a$2368a6e0$0202040a@catdog>
On Dec 17, 3:23pm, Kris Warkentin wrote:
> I recently came across a problem debugging a core file with some of our
> older shared libs. Info shared showed the relocations of the shared libs to
> be mangled (offset to 0x60... range rather than 0xb0... range). We had
> recently changed our tools to always set the vaddr of shared libs to be zero
> because of this but I was speaking to one of our architects and he says that
> this shouldn't be.
>
> One of the future optimizations we're looking at is pre-relocating shared
> libs so that they can be executed in place (on flash for instance) and the
> fact that the SysV stuff seems to assume that everything is based at zero is
> not particularily compatable with that. I've attached an ugly patch that
> shows a fix. This is for illustration only since solib.c is the wrong place
> to put this but it makes it clear what the issue is.
>
> Can anyone with more knowledge than I enlighten me as to a) whether it is
> proper to allow shared objects to be non zero-based and b) a better way to
> do this. I looked at putting it in solib-svr4.c but I don't have access to
> the bfd in there, at least in svr4_relocate_section_addresses().
With regard to a), it's okay for shared objects to be non-zero based.
We'll get to b) in a bit...
Let's first take a look at the existing code in GDB's solib-svr4.c:
static void
svr4_relocate_section_addresses (struct so_list *so,
struct section_table *sec)
{
sec->addr = svr4_truncate_ptr (sec->addr + LM_ADDR (so));
sec->endaddr = svr4_truncate_ptr (sec->endaddr + LM_ADDR (so));
}
This code is adjusting the start and end addresses by LM_ADDR (so) which
is simply the l_addr field obtained from struct link_map.
On my i386 GNU/Linux system, the comment in link.h says that l_addr is
the "base address shared object is loaded at." We must be very careful
with the term "base address" because it's quite possible that it means
something very different than what you think it does. (I certainly found
it counter-intuitive.)
Here is what chapter 5 of the "System V Application Binary Interface,
Edition 4.1" says about the term "Base Address":
As "Program Loading" in this chapter of the processor supplement
describes, the virtual addresses in the program headers might not
represent the actual virtual addresses of the program's memory
image. Executable files typically contain absolute code. To let
the process execute correctly, the segments must reside at the
virtual addresses used to build the executable file. On the other
hand, shared object segments typically contain
position-independent code. This lets a segment's virtual address
change from one process to another, without invalidating execu-
tion behavior. Though the system chooses virtual addresses for
individual processes, it maintains the segments' relative
positions. Because position- independent code uses relative
addressing between segments, the difference between virtual
addresses in memory must match the difference between virtual
addresses in the file. The difference between the virtual address
of any segment in memory and the corresponding virtual address in
the file is thus a single constant value for any one executable or
shared object in a given process. This difference is the base
address. One use of the base address is to relocate the memory
image of the program during dynamic linking.
An executable or shared object file's base address is calculated
during execution from three values: the virtual memory load
address, the maximum page size, and M the lowest virtual address
of a program's loadable segment. To compute the base address, one
determines the memory address associated with the lowest p_vaddr
value for a PT_LOAD segment. This address is truncated to the
nearest multiple of the maximum page size. The corresponding
p_vaddr value itself is also truncated to the nearest multiple of
the maximum page size. The base address is the difference between
the truncated memory address and the truncated p_vaddr value.
See this chapter in the processor supplement for more information
and examples. "Operating System Interface" of Chapter 3 in the
processor supplement contains more information about the virtual
address space and page size.
So, as I understand it, the "base address" is *not* an absolute
location, but is actually the difference between where the segment
ended up and the location assigned to it in the executable file.
Thus the "base address" is the proper value to use to relocate the
segment's start and end addresses.
Now, it's possible that my understanding is flawed. If so, I await
enlightenment. I think it's also possible that the shared library
implementation that you're using might not conform to the above
definition of "base address". If that's the case, then you can either
attempt to get it fixed in the code which implements the dynamic
loader, or, if that's not possible, create a new solib backend for gdb
which implements support for your shared library machinery. I suspect
it would be very similar to solib-svr4.c.
With regard to the two patches that have been posted for fixing this
problem, I don't think that either one is correct in light of the
above definition of "base address".
Kevin