This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Q: ld scripts for multisegment ELF dynamic executables?
- To: binutils at sourceware dot cygnus dot com
- Subject: Q: ld scripts for multisegment ELF dynamic executables?
- From: Basile STARYNKEVITCH <basile dot starynkevitch at wanadoo dot fr>
- Date: Sun, 25 Feb 2001 01:02:48 +0100 (CET)
Hello All binutils power users and developpers,
My plateform is Linux/Debian (testing=sid) x86 (AMD/K6 II/400), kernel
2.2.18. I used both the debian ld (GNU ld 2.10.91) and a freshly
compiled and installed (in /usr/local) binutils-2.10.91.0.2.tar.gz
I am computer educated and knows quite well the ELF format; but I do
not know the internals of binutils and ld.
My goal is to produce an ELF dynamically linked executable with more
than the 2 usual text and data segments (segments are what describes
the ELF program header, and are memory mapped by the execve system
call).
I would like to have four segments (excluding the PHDR and DYNAMIC) -
the 2 usual ones and 2 own ones.
the usual text (read-only, executable) segment (containing code and
readonly data)
the usual data (read & write) segment (containing initialized
[.data...] and unitialized [.bss]
my own TEXTHEAP segment is read-only, executable, and should start
at a fixed virtual memory address (say 0x60000000)
my own DATAHEAP segment is read and write and executable, and should
start at a fixed virtual memory address (say 0x70000000)
The intent is to have an ELF executable (linked ot shared objects such
as /lib/libc.so.6 and /usr/X11/lib/libX11.so*) which contains C
compiled code and data (e.g. some GUI code using gtk and X11) and also
a "persisted" heap of a Lisp-like (compiled and garbage-collected)
language implementation. The rationale of having my own segments (and
not using for my heap the usual data and text segments) at fixed
addresses is that exporting my heap (with eventually its relocation)
would be much easier than the unexecelf routine of Emacs or Xemacs or
the dldump call available on Solaris.
I have no doubt that the Linux kernel supports more than 2 segments
(in the execve system call and kernel source file
linux/fs/binfmt_elf.c).
I tried to code the following ld script (which is an extension of
/usr/lib/ldscripts/elf_i386.x) :::
############################################################
/* file Qish/Basic/ldscript */
/* heavily copied from /usr/lib/ldscripts/elf_i386.x on my Debian/sid system */
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
"elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/X11R6/lib);
textheap_base = ABSOLUTE(0x60000000) ;
dataheap_base = ABSOLUTE(0x70000000) ;
PHDRS {
headers PT_PHDR PHDRS ;
interp PT_INTERP ;
text PT_LOAD FILEHDR PHDRS ;
data PT_LOAD ;
dynamic PT_DYNAMIC ;
note PT_NOTE ;
textheap PT_LOAD AT (0x60000000) FLAGS (5) /*r-x*/;
dataheap PT_LOAD AT (0x70000000) FLAGS (7) /*rwx*/ ;
}
SECTIONS
{
/***************** plain text segment ****************/
/* Read-only sections, merged into text segment: */
. = 0x08048000 + SIZEOF_HEADERS;
.interp : { *(.interp) } :text :interp
.hash : { *(.hash) } :text
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rela.init : { *(.rela.init) }
.rel.text :
{
*(.rel.text)
*(.rel.text.*)
*(.rel.gnu.linkonce.t.*)
}
.rela.text :
{
*(.rela.text)
*(.rela.text.*)
*(.rela.gnu.linkonce.t.*)
}
.rel.fini : { *(.rel.fini) }
.rela.fini : { *(.rela.fini) }
.rel.rodata :
{
*(.rel.rodata)
*(.rel.rodata.*)
*(.rel.gnu.linkonce.r.*)
}
.rela.rodata :
{
*(.rela.rodata)
*(.rela.rodata.*)
*(.rela.gnu.linkonce.r.*)
}
.rel.data :
{
*(.rel.data)
*(.rel.data.*)
*(.rel.gnu.linkonce.d.*)
}
.rela.data :
{
*(.rela.data)
*(.rela.data.*)
*(.rela.gnu.linkonce.d.*)
}
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.sdata :
{
*(.rel.sdata)
*(.rel.sdata.*)
*(.rel.gnu.linkonce.s.*)
}
.rela.sdata :
{
*(.rela.sdata)
*(.rela.sdata.*)
*(.rela.gnu.linkonce.s.*)
}
.rel.sbss :
{
*(.rel.sbss)
*(.rel.sbss.*)
*(.rel.gnu.linkonce.sb.*)
}
.rela.sbss :
{
*(.rela.sbss)
*(.rela.sbss.*)
*(.rel.gnu.linkonce.sb.*)
}
.rel.sdata2 :
{
*(.rel.sdata2)
*(.rel.sdata2.*)
*(.rel.gnu.linkonce.s2.*)
}
.rela.sdata2 :
{
*(.rela.sdata2)
*(.rela.sdata2.*)
*(.rela.gnu.linkonce.s2.*)
}
.rel.sbss2 :
{
*(.rel.sbss2)
*(.rel.sbss2.*)
*(.rel.gnu.linkonce.sb2.*)
}
.rela.sbss2 :
{
*(.rela.sbss2)
*(.rela.sbss2.*)
*(.rela.gnu.linkonce.sb2.*)
}
.rel.bss :
{
*(.rel.bss)
*(.rel.bss.*)
*(.rel.gnu.linkonce.b.*)
}
.rela.bss :
{
*(.rela.bss)
*(.rela.bss.*)
*(.rela.gnu.linkonce.b.*)
}
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
.init :
{
KEEP (*(.init))
} =0x9090
.plt : { *(.plt) }
.text :
{
*(.text)
*(.text.*)
*(.stub)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.gnu.linkonce.t.*)
} =0x9090
.fini :
{
KEEP (*(.fini))
} =0x9090
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata) *(.rodata.*) *(.gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.sdata2 : { *(.sdata2) *(.sdata2.*) *(.gnu.linkonce.s2.*) }
.sbss2 : { *(.sbss2) *(.sbss2.*) *(.gnu.linkonce.sb2.*) }
/**************** plain data segment ****************/
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = ALIGN(0x1000) + (. & (0x1000 - 1));
.data :
{
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
} :data
.data1 : { *(.data1) }
.eh_frame : { KEEP (*(.eh_frame)) }
.gcc_except_table : { *(.gcc_except_table) }
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
/* We don't want to include the .ctor section from
from the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.got : { *(.got.plt) *(.got) }
_edata = .;
PROVIDE (edata = .);
__bss_start = .;
.sbss :
{
PROVIDE (__sbss_start = .);
PROVIDE (___sbss_start = .);
*(.dynsbss)
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
*(.scommon)
PROVIDE (__sbss_end = .);
PROVIDE (___sbss_end = .);
}
.bss :
{
*(.dynbss)
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections. */
. = ALIGN(32 / 8);
}
. = ALIGN(32 / 8);
_end = .;
PROVIDE (end = .);
.dynamic :
{ KEEP(*(.dynamic)) } :data :dynamic
/***************** read-only text heap *****************/
. = ALIGN(0x10000);
TEXTHEAP textheap_base
() : {
/*0xF4 is the priviledged HLT instruction*/
LONG(0xF4F4F4F4);
LONG(0xF4F4F4F4);
FILL(0xF4F4F4F4);
textheap_start = . ;
KEEP(*(TEXTHEAP))
LONG(0xF4F4F4F4);
LONG(0xF4F4F4F4);
FILL(0);
} :textheap
textheap_end = . ;
. = ALIGN(0x100);
.rel.TEXTHEAP : {
*(.rel.TEXTHEAP)
}
. = ALIGN(0x100);
.rel.DATAHEAP : {
*(.rel.DATAHEAP)
}
/***************** read-write data heap *****************/
. = ALIGN(0x10000);
DATAHEAP dataheap_base
(): {
dataheap_start = . ;
KEEP(*(DATAHEAP))
LONG(0);
FILL(0);
dataheap_end = . ;
} :dataheap
. = ALIGN(0x1000);
/***************** debugging and info ****************/
.note : {
*(.note)
*(.note.*)
} :text :note
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections are not needed */
}
############################################################
I tried this tiny main2.c program
/* file Qish/main2.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
int main(int argc, char** argv)
{
static char line[400];
puts("in Qish/main2.c::" __DATE__ "," __TIME__);
if (argc>1) {
FILE* f=0;
void *h= dlopen(argv[1], RTLD_NOW);
printf("after dlopen %s h=%p\n", argv[1], h);
f = fopen("/proc/self/maps", "r");
if (f) {
while (!feof(f)) {
fgets(line, sizeof(line), f);
fputs(line, stdout);
};
fclose(f);
};
}
return 0;
}
############################################################
I compiled main2.c with
gcc -g -O -c main2.c
then I linked with
ld -export-dynamic -dynamic-linker /lib/ld-linux.so.2 -o /tmp/q2 \
/usr/lib/crt1.o /usr/lib/crti.o \
/usr/lib/gcc-lib/i386-linux/2.95.3/crtbegin.o \
-L/usr/lib/gcc-lib/i386-linux/2.95.3 main2.o -ldl -lm /lib/libc.so.6 \
-lgcc -lc -lgcc /usr/lib/gcc-lib/i386-linux/2.95.3/crtend.o \
/usr/lib/crtn.o -T ldscript
I've got no compile- or link-time errors. But running the binary
/tmp/q2 core dumps. And even running ldd /tmp/q2 gives ldd:
/lib/ld-linux.so.2 exited with unknown exit code (139)
Any clues or hints?
(perhaps giving me the default PHDRS of ld might help me).
Regards
--
Basile STARYNKEVITCH -- http://perso.wanadoo.fr/starynkevitch/basile/
email: basile dot starynkevitch at wanadoo dot fr (France)
alias: basile at tunes dot org host: http://lesours.dyndns.org/
8, rue de la Faïencerie, 92340 Bourg La Reine, phone: 1.46.65.45.53