Proof of concept/RFC: Reordering of init functions at build time

Joerg Pommnitz (pommnitz@darmstadt.gmd.de)
Fri, 13 Aug 1999 01:57:25 +0200


This is a multi-part message in MIME format.
--------------59BAF9427D7900541FEC5EBF
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Attached please find a patch that implements my proposed
way to solve the initialization-oder problem for functions in the
.initcall.init section.
It introduces an additional step in the build process that reorders
init functions according to an external specification. It is has
been tested on x86 Linux. It should work on all 32bit architectures.
For 64bit architectures, the fiximage program has to be modified
to support ELF64.
Im interested in comments/suggestions. I hope, that once this has
been reviewed, my code can be included into the mainline kernel.

Attached is an example from the last stage of the build of 2.3.13
on my machine. The initdependencies file contains the following line:
device_setup: piix4_idle_init kswapd_init

Observe the change of the order in the initialization table.
The original order is:
piix4_idle_init 0xc0210a90
kswapd_init 0xc021310c
bdflush_init 0xc0213424
init_elf_binfmt 0xc02136e4
init_aout_binfmt 0xc0213704
init_script_binfmt 0xc0213724
device_setup 0xc0214e10
parport_init 0xc0217a70
pci_proc_init 0xc021c2c4

The modified order is:

device_setup
kswapd_init
piix4_idle_init
bdflush_init
init_elf_binfmt
init_aout_binfmt
init_script_binfmt
parport_init
pci_proc_init

As you can see, the functions listed in the dependcy description moved
to the top whereas the unlisted functions remained in the original
order.
The onliest other remark I have to make is, that the binary patched
kernel still boots and the system seems stable.

--------------------start final part of build
process---------------------------------------
nm vmlinux | grep -v '\(compiled\)\|\(\.o$\)\|\( [aU]
\)\|\(\.\.ng$\)\|\(LASH[RL]DI\)' | sort > System.map
cat `find /usr/src/linux -name initdependencies -print` |
/usr/src/linux/scripts/catdep | tsort | /usr/src/linux/scripts/symaddr
-f /usr/src/linux/System.map | /usr/src/linux/scripts/fiximage -d -f
/usr/src/linux/vmlinux
name is 0,
name is 27, .text
name is 33, .text.lock
name is 44, .rodata
name is 52, .kstrtab
name is 61, __ex_table
name is 72, __ksymtab
name is 82, .data
name is 88, .data.init_task
name is 104, .text.init
name is 115, .data.init
name is 126, .setup.init
name is 138, .initcall.init
0000 -> 0xc0210a90
0001 -> 0xc021310c
0002 -> 0xc0213424
0003 -> 0xc02136e4
0004 -> 0xc0213704
0005 -> 0xc0213724
0006 -> 0xc0214e10
0007 -> 0xc0217a70
0008 -> 0xc021c2c4
0000 -> 0xc0214e10
0001 -> 0xc021310c
0002 -> 0xc0210a90
0003 -> 0xc0213424
0004 -> 0xc02136e4
0005 -> 0xc0213704
0006 -> 0xc0213724
0007 -> 0xc0217a70
0008 -> 0xc021c2c4
1496980
make[1]: Entering directory `/usr/src/linux/arch/i386/boot'
make[2]: Entering directory `/usr/src/linux/arch/i386/boot/compressed'
tmppiggy=_tmp_$$piggy; \
rm -f $tmppiggy $tmppiggy.gz $tmppiggy.lnk; \
objcopy -O binary -R .note -R .comment -S /usr/src/linux/vmlinux
$tmppiggy; \
gzip -f -9 < $tmppiggy > $tmppiggy.gz; \
echo "SECTIONS { .data : { input_len = .; LONG(input_data_end -
input_data) input_data = .; *(.data) input_data_end = .; }}" >
$tmppiggy.lnk; \
ld -m elf_i386 -m elf_i386 -r -o piggy.o -b binary $tmppiggy.gz -b
elf32-i386 -T $tmppiggy.lnk; \
rm -f $tmppiggy $tmppiggy.gz $tmppiggy.lnk
ld -m elf_i386 -Ttext 0x100000 -e startup_32 -o bvmlinux head.o misc.o
piggy.o
make[2]: Leaving directory `/usr/src/linux/arch/i386/boot/compressed'
objcopy -O binary -R .note -R .comment -S compressed/bvmlinux
compressed/bvmlinux.out
tools/build -b bbootsect bsetup compressed/bvmlinux.out CURRENT >
bzImage
Root device is (3, 6)
Boot sector 512 bytes.
Setup is 3444 bytes.
System is 556 kB
-------------------------------End of build
process---------------------------------

Regards
Joerg
--------------59BAF9427D7900541FEC5EBF
Content-Type: text/plain; charset=us-ascii;
name="linux.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="linux.diff"

Binary files linux/.System.map.swp and linux.jpo/.System.map.swp differ
diff -ruN linux/arch/i386/Makefile linux.jpo/arch/i386/Makefile
--- linux/arch/i386/Makefile Tue Jul 13 21:26:46 1999
+++ linux.jpo/arch/i386/Makefile Fri Aug 13 00:22:16 1999
@@ -16,6 +16,10 @@
# Added '-march' and '-mpreferred-stack-boundary' support
#

+SCRIPTSDIR=$(TOPDIR)/scripts
+FIXIMAGE=$(SCRIPTSDIR)/fiximage
+CATDEP=$(SCRIPTSDIR)/catdep
+SYMADDR=$(SCRIPTSDIR)/symaddr
LD=$(CROSS_COMPILE)ld -m elf_i386
CPP=$(CC) -E
OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
@@ -77,7 +81,16 @@

MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot

-vmlinux: arch/i386/vmlinux.lds
+vmlinux: arch/i386/vmlinux.lds $(FIXIMAGE) $(CATDEP) $(SYMADDR)
+
+$(FIXIMAGE): $(SCRIPTSDIR)/fiximage.c
+ $(HOSTCC) -o $@ $^ -lelf
+
+$(CATDEP): $(SCRIPTSDIR)/catdep.c
+ $(HOSTCC) -o $@ $^
+
+$(SYMADDR): $(SCRIPTSDIR)/symaddr.c
+ $(HOSTCC) -o $@ $^

arch/i386/vmlinux.lds: arch/i386/vmlinux.lds.S FORCE
gcc -E -C -P -I$(HPATH) -imacros $(HPATH)/asm-i386/page_offset.h -Ui386 arch/i386/vmlinux.lds.S >arch/i386/vmlinux.lds
@@ -88,6 +101,7 @@
@$(MAKEBOOT) zImage

bzImage: vmlinux
+ cat `find $(TOPDIR) -name initdependencies -print` | $(CATDEP) | tsort | $(SYMADDR) -f $(TOPDIR)/System.map | $(FIXIMAGE) -d -f $(TOPDIR)/vmlinux
@$(MAKEBOOT) bzImage

compressed: zImage
diff -ruN linux/initdependencies linux.jpo/initdependencies
--- linux/initdependencies Thu Jan 1 01:00:00 1970
+++ linux.jpo/initdependencies Fri Aug 13 00:37:31 1999
@@ -0,0 +1 @@
+device_setup: piix4_idle_init kswapd_init
\ No newline at end of file
diff -ruN linux/scripts/catdep.c linux.jpo/scripts/catdep.c
--- linux/scripts/catdep.c Thu Jan 1 01:00:00 1970
+++ linux.jpo/scripts/catdep.c Thu Aug 12 23:57:44 1999
@@ -0,0 +1,53 @@
+/*
+ * Author: Joerg Pommnitz (pommnitz@darmstadt.gmd.de)
+ *
+ * Copyright (C) 1999 Joerg Pommnitz
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define BUFSIZE (16 * 1024)
+const char *WS = "\t \n";
+
+int
+main (int argc, char *argv[])
+{
+ FILE *f = 0;
+ char buf[BUFSIZE];
+
+ if (argc == 2) {
+ if ((f = fopen (argv[1], "rb")) == 0) {
+ fprintf (stderr, "Invalid file name %s\n", argv[1]);
+ exit (1);
+ }
+ } else {
+ f = stdin;
+ }
+
+ while (fgets (buf, BUFSIZE, f)) {
+ char *start_nodes, *end_nodes;
+ char *curr;
+ int i, num_end = 0;
+ char *end_tokens[BUFSIZE];
+
+ start_nodes = buf;
+ end_nodes = strchr (buf, ':');
+
+ *end_nodes++ = 0;
+
+ for (curr = strtok (end_nodes, WS); curr; curr = strtok (0, WS), num_end++) {
+ end_tokens[num_end] = curr;
+ }
+
+ for (curr = strtok (start_nodes, WS); curr; curr = strtok (0, WS)) {
+ for (i = 0; i < num_end; i++) {
+ printf ("%s %s\n", curr, end_tokens[i]);
+ }
+ }
+ }
+
+ return 0;
+}
diff -ruN linux/scripts/fiximage.c linux.jpo/scripts/fiximage.c
--- linux/scripts/fiximage.c Thu Jan 1 01:00:00 1970
+++ linux.jpo/scripts/fiximage.c Fri Aug 13 00:25:23 1999
@@ -0,0 +1,208 @@
+/*
+ * Tool for dependecy fixup in the Linux kernel
+ * initialization sequence
+ *
+ * Author: Joerg Pommnitz (pommnitz@darmstadt.gmd.de)
+ *
+ * Copyright (C) 1999 Joerg Pommnitz
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <elf.h>
+#include <libelf/libelf.h>
+
+#define MAXSYMBOLS (20 * 1024)
+#define LINESIZE 256
+
+#define NOADDR ((void *)0xffffffff)
+
+static char *ImageFileName = 0;
+static char *SectionName = 0;
+static FILE *GoalFile = 0;
+static int DebugFlag = 0;
+
+void Usage (void)
+{
+ fprintf (stderr, "fiximage [-f file] [-s section] [-g input]\n");
+ exit (1);
+}
+
+void
+ParseArgv (int argc, char *argv[])
+{
+ int c;
+
+ while ((c = getopt (argc, argv, "f:s:g:d")) != -1) {
+ switch (c) {
+ case 'd':
+ DebugFlag++;
+ break;
+ case 'f':
+ ImageFileName = strdup (optarg);
+ break;
+ case 's':
+ SectionName = strdup (optarg);
+ break;
+ case 'g':
+ if ((GoalFile = fopen (optarg, "r")) == 0) {
+ fprintf (stderr, "Could not open file %s\n", optarg);
+ }
+ break;
+ case '?':
+ Usage ();
+ }
+ }
+
+ if (ImageFileName == 0) {
+ ImageFileName = "vmlinux";
+ }
+
+ if (SectionName == 0) {
+ SectionName = ".initcall.init";
+ }
+
+ if (GoalFile == 0) {
+ GoalFile = stdin;
+ }
+}
+
+
+int
+ElfInit (char *path, Elf **elf, Elf32_Half *ndx)
+{
+ Elf32_Ehdr *ehdr;
+ int fd = open (path, O_RDWR);
+
+ if (fd < 0) {
+ perror ("open");
+ return 1;
+ }
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ fprintf (stderr, "elf_version: library out of date\n");
+ return 1;
+ }
+
+ if ((*elf = elf_begin(fd, ELF_C_RDWR, 0)) == 0) {
+ fprintf (stderr, "elf_begin failed\n");
+ return 1;
+ }
+
+ if ((ehdr = elf32_getehdr(*elf)) == 0) {
+ fprintf (stderr, "elf32_getehdr failed\n");
+ return 1;
+ }
+
+ *ndx = ehdr->e_shstrndx;
+
+ return 0;
+}
+
+Elf_Scn *
+GetSectionByName (Elf *elf, Elf32_Half str_scn, char *name)
+{
+ Elf_Scn *scn;
+
+ for (scn = elf_getscn(elf, 0); scn; scn = elf_nextscn(elf, scn)) {
+ Elf32_Shdr *hdr = elf32_getshdr(scn);
+ char *current_name = elf_strptr (elf, str_scn, hdr->sh_name);
+
+ if (DebugFlag)
+ printf ("name is %d, %s\n", hdr->sh_name, current_name);
+
+ if (strcmp (name, current_name) == 0)
+ return scn;
+ }
+
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ Elf *elf;
+ Elf32_Half str_scn;
+ Elf_Scn *scn;
+ Elf_Data *data;
+ void **functs;
+ void **reordered;
+ void *DestOrder[MAXSYMBOLS];
+ char buf[LINESIZE];
+ int num_entries, i, j;
+
+ ParseArgv (argc, argv);
+
+ for (num_entries = 0; fgets (buf, LINESIZE, GoalFile); num_entries++) {
+ sscanf (buf, "%p\n", DestOrder + num_entries);
+ }
+
+ if (ElfInit (ImageFileName, &elf, &str_scn) != 0) {
+ return -1;
+ }
+
+ scn = GetSectionByName (elf, str_scn, SectionName);
+
+ if (scn == 0) {
+ fprintf (stderr, "could not find ELF section %s\n", SectionName);
+ exit (1);
+ }
+
+ data = elf_getdata(scn, 0);
+
+ reordered = malloc (data->d_size);
+
+ /* special hack for the .ctors section: the real constructor
+ table seems to be enclosed in 0x00000000 and 0xffffffff */
+
+ if (strcmp (SectionName, ".ctors") == 0) {
+ reordered[0] = (void *)(0xffffffff);
+ j = 1;
+ } else {
+ j = 0;
+ }
+
+ if (DebugFlag) {
+ for (functs = data->d_buf, i = 0; &functs[i] < (void **)(data->d_buf + data->d_size); i++) {
+ fprintf (stderr, "%04d -> %p\n", i, functs[i]);
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ for (functs = data->d_buf; functs < (void **)(data->d_buf + data->d_size); functs++) {
+ if (DestOrder [i] == *functs) {
+ reordered[j++] = *functs;
+ *functs = NOADDR;
+ break;
+ }
+ }
+ }
+
+ for (functs = data->d_buf; functs < (void **)(data->d_buf + data->d_size); functs++) {
+ if (*functs != NOADDR) {
+ reordered[j++] = *functs;
+ }
+ }
+
+ memcpy (data->d_buf, reordered, data->d_size);
+
+ if (DebugFlag) {
+ for (functs = data->d_buf, i = 0; &functs[i] < (void **)(data->d_buf + data->d_size); i++) {
+ fprintf (stderr, "%04d -> %p\n", i, functs[i]);
+ }
+ }
+
+ if (DebugFlag)
+ printf ("%d\n", elf_update(elf, ELF_C_WRITE));
+
+ elf_end(elf);
+
+ return 0;
+}
diff -ruN linux/scripts/symaddr.c linux.jpo/scripts/symaddr.c
--- linux/scripts/symaddr.c Thu Jan 1 01:00:00 1970
+++ linux.jpo/scripts/symaddr.c Thu Aug 12 23:57:50 1999
@@ -0,0 +1,107 @@
+/*
+ * Author: Joerg Pommnitz (pommnitz@darmstadt.gmd.de)
+ *
+ * Copyright (C) 1999 Joerg Pommnitz
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BUFSIZE (16 * 1024)
+#define LINESIZE (16 * 1024)
+
+struct mapping {
+ void *sym_addr;
+ char sym_name[128];
+} mapping_buf[BUFSIZE];
+
+static FILE *SymbolFile = 0;
+static FILE *QueryFile = 0;
+
+int
+CompareFunc (const void *e1, const void *e2)
+{
+ const struct mapping *p1 = e1, *p2 = e2;
+
+ return strcmp (p1->sym_name, p2->sym_name);
+}
+
+void
+Usage (void)
+{
+ fprintf (stderr, "Usage: symaddr -f symbol_file\n");
+ exit (1);
+}
+
+void
+ParseArgv (int argc, char *argv[])
+{
+ int c;
+
+ while ((c = getopt (argc, argv, "f:")) != -1) {
+ switch (c) {
+ case 'f':
+ if ((SymbolFile = fopen (optarg, "rb")) == 0) {
+ fprintf (stderr, "Could not open symbol file %s!\n", optarg);
+ exit (1);
+ }
+ break;
+ case 'q':
+ if ((QueryFile = fopen (optarg, "rb")) == 0) {
+ fprintf (stderr, "Could not open query file %s!\n", optarg);
+ exit (1);
+ }
+ break;
+ case '?':
+ Usage ();
+ }
+ }
+
+ if (SymbolFile == 0) {
+ Usage ();
+ }
+
+ if (QueryFile == 0) {
+ QueryFile = stdin;
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ char buf[LINESIZE] = {'0','x',};
+ int num_mappings, i;
+
+ ParseArgv (argc, argv);
+
+ for (num_mappings = 0; fgets (buf + 2, LINESIZE, SymbolFile); num_mappings++) {
+ char dummy;
+ sscanf (buf, "%p %c %s\n",
+ &mapping_buf[num_mappings].sym_addr,
+ &dummy, mapping_buf[num_mappings].sym_name);
+ }
+
+ qsort (mapping_buf, num_mappings, sizeof (mapping_buf[0]), CompareFunc);
+
+ while (fgets (buf, LINESIZE, QueryFile)) {
+ struct mapping key;
+ struct mapping *res;
+ char *p;
+
+ if (p = strrchr (buf, '\n')) {
+ *p = 0;
+ }
+
+ strcpy (key.sym_name, buf);
+
+ if ((res = bsearch (&key, mapping_buf, num_mappings,
+ sizeof (mapping_buf[0]), CompareFunc)) != 0) {
+ printf ("%p\n", res->sym_addr);
+ }
+ }
+
+ return 0;
+}

--------------59BAF9427D7900541FEC5EBF--

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/