RFC: should objcopy convert .rel.* to .rela and back?
Alexey Neyman
stilor@att.net
Tue Aug 21 16:31:00 GMT 2018
More information about the Binutils mailing list
Tue Aug 21 16:31:00 GMT 2018
- Previous message (by thread): [patches] Re: [PATCH] RISC-V: Reject empty rouding mode and fence operand.
- Next message (by thread): RFC: should objcopy convert .rel.* to .rela and back?
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
[Resend due to no response]
Hi all,
In our product, we have some 32- and 64-bit x86 code within a single
binary. They share the data structures so it is convenient to link them
together. This is done by compiling the 32-bit code and then converting
it to 64-bit using `objcopy -Oelf64-x86-64 input.o output.o` and then
using output.o to link with 64-bit parts.
The problem with this approach, though, is that the i386 and x86-64 ABIs
mandate use of different formats for the relocation sections. i386 ABI
uses .rel.*, while x86-64 prescribes using .rela.* (in relocatable
objects). The objcopy command above, however, does not change the format
of the relocation sections: the resulting output.o, despite being an
x86-64 object, contains .rel.* sections.
This in turn leads to a problem during incremental link when the linker
needs to update the addends in the relocations. When linking "regular"
x86-64 files, ld updates the r_addend fields in the respective .rela.*
sections. When linking "regular" i386 files, ld updates the values
within the section against which the relocation is made. But when
linking such "converted" files, ld does neither! Apparently, it wants to
update r_addend fields because it links for x86-64 - but there is no
.rela.* sections. The resulting object has bogus relocations for 2nd and
subsequent object files.
That means, this approach can currently be used only if there is a
single instance of such "converted" object in the link, and that
"converted" object is the first linker's input (in this case, the
addends remain the same - so they're valid even if ld does not update
them). What bothers me is that if that assumption does not hold, ld
silently creates a bogus object. I attached a script that reproduces the
issue.
Hence, two questions:
1. Should objcopy have an ability to convert .rel.* to .rela.* and back?
I am thinking of something like: --reloc-format=[native|keep|rel|rela],
with default being either "keep" (the current behavior) or "native"
(converting to the default relocation format for the output BFD - which,
IMO, makes the most sense).
2. Shouldn't ld detect that it must update the addends and has no way to
do so?
Regards,
Alexey.
-------------- next part --------------
#!/bin/bash
GCC=${CROSS_PREFIX}gcc
LD=${CROSS_PREFIX}ld
OBJCOPY=${CROSS_PREFIX}objcopy
READELF=${CROSS_PREFIX}readelf
HEXDUMP=${HEXDUMP-hexdump -C}
print_reloc_section()
{
local file=$1
local state=0
$READELF -Wr $file | while read i; do
case "$i" in
"Relocation section '.rel"*"$RODATA'"*)
state=1
i=`echo "$i" | sed -e "s,^,=> ," -e "s, at offset .*, in file $file,"`
;;
"")
if [ "$state" = 1 ]; then
break
fi
;;
esac
if [ "$state" = 1 ]; then
echo "$i"
fi
done
}
print_section()
{
local file=$1
$OBJCOPY -j $RODATA -Obinary $file $file.rodata
echo "=> Section '$RODATA' in file $file"
$HEXDUMP $file.rodata
}
# Determine the section name
cat >s.c <<EOF
const void * const x = &x;
EOF
$GCC -c s.c
RODATA=`$READELF -WS s.o | sed -n 's/.* \.rela\(\.[a-z.]*\) .*/\1/p'`
cat >t.c <<EOF
static void F(void) {}
void (* const P)(void) = F;
EOF
cat <<EOF
| Each of the tests compiles two instances of the test code
| and then incrementally links them together. Each of the two
| input object has a function in .text section and a pointer
| to that function in .rodata section: f1/p1 in one object, and
| f2/p2 in the other.
|
| What is different is that we either convert 32-bit code to 64-bit
| after the link, before the link, or just compile in 64-bit
| right away. The objects are prefixed with t32a, t32b and t64,
| respectively.
EOF
cat <<EOF
|
| TEST 1
|-----------------------------------------------------------
| Compile and link to 64-bit directly. The resulting object has two
| pointers to .text in the .rodata, with different r_addend fields in the
| .rela.rodata section and zero (ignored) fields in .rodata.
|
EOF
$GCC -c t.c -DF=f1 -DP=p1 -o t64-1.o
$GCC -c t.c -DF=f2 -DP=p2 -o t64-2.o
$LD -r -o t64.o t64-1.o t64-2.o
print_reloc_section t64.o
print_section t64.o
cat <<EOF
|
| TEST 2
|-----------------------------------------------------------
| Compile and link in 32-bit mode, then convert to 64-bit. The resulting
| file has a .rel.rodata section (still violating the x86-64 SysV ABI
| requirement, which requires x86-64 use .rela.* sections only in relocatable
| files) but at least the contents of the .rodata and .rel.rodata is correct:
|
EOF
$GCC -m32 -c t.c -DF=f1 -DP=p1 -o t32a-1.o
$GCC -m32 -c t.c -DF=f2 -DP=p2 -o t32a-2.o
$LD -melf_i386 -r -o t32a.tmp.o t32a-1.o t32a-2.o
$OBJCOPY -Oelf64-x86-64 t32a.tmp.o t32a.o
print_reloc_section t32a.o
print_section t32a.o
cat <<EOF
|
| This happens because the linker did update the addend within .rodata in
| the intermediate 32-bit file as part of the incremental link (note the
| .rodata is zeros in both input files, but has non-zero offset in the
| second pointer in the resulting file).
EOF
print_section t32a-1.o
print_section t32a-2.o
print_section t32a.tmp.o
cat <<EOF
|
| TEST 3
|-----------------------------------------------------------
| Compile in 32-bit mode, then convert to 64-bit, then link the
| resulting files. In addition to the section type (.rel vs .rela)
| being wrong, the linker has updated addends in neither .rodata
| nor .rel.rodata sections!
|
EOF
$GCC -m32 -c t.c -DF=f1 -DP=p1 -o t32b-1.tmp.o
$OBJCOPY -Oelf64-x86-64 t32b-1.tmp.o t32b-1.o
$GCC -m32 -c t.c -DF=f2 -DP=p2 -o t32b-2.tmp.o
$OBJCOPY -Oelf64-x86-64 t32b-2.tmp.o t32b-2.o
$LD -r -o t32b.o t32b-1.o t32b-2.o
print_reloc_section t32b.o
print_section t32b.o
cat <<EOF
|
| Apparently, during the incremental link, LD cannot update the r_addend
| in .rel.rodata - because r_addend field only exists in .rela.* sections.
| And apparently because it was linking for x86-64, it didn't update the
| contents of .rodata either. The result is that the "p2" pointer now points
| to "f1" function instead of "f2".
EOF
- Previous message (by thread): [patches] Re: [PATCH] RISC-V: Reject empty rouding mode and fence operand.
- Next message (by thread): RFC: should objcopy convert .rel.* to .rela and back?
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Binutils mailing list