Commit cbc98ae5 authored by Kai Germaschewski's avatar Kai Germaschewski

kbuild: Figure endianness / word size at compile time

The helper file2alias had to jump through a lot of hoops because it did not
know the host and target endianness / word size at compile time.
We now build a small header scripts/elfconfig.h which contains this
information for file2alias to use.
parent 0e5064f8
......@@ -7,19 +7,18 @@
#ifndef LINUX_MOD_DEVICETABLE_H
#define LINUX_MOD_DEVICETABLE_H
#include <linux/types.h>
#ifdef __KERNEL__
#include <linux/types.h>
typedef unsigned long kernel_ulong_t;
#endif
#define PCI_ANY_ID (~0)
struct pci_device_id {
unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */
unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */
kernel_ulong_t driver_data; /* Data private to the driver */
__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/
__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
__u32 class, class_mask; /* (class,subclass,prog-if) triplet */
kernel_ulong_t driver_data; /* Data private to the driver */
};
/*
......
......@@ -9,11 +9,23 @@
# conmakehash: Create arrays for initializing the kernel console tables
host-progs := fixdep split-include conmakehash docproc kallsyms modpost \
file2alias
build-targets := $(host-progs)
file2alias mk_elfconfig
build-targets := $(host-progs) empty.o
# Let clean descend into subdirs
subdir- := lxdialog kconfig
# fixdep is needed to compile other host programs
$(addprefix $(obj)/,$(filter-out fixdep,$(host-progs))): $(obj)/fixdep
# dependencies on generated files need to be listed explicitly
$(obj)/file2alias: $(obj)/elfconfig.h
quiet_cmd_elfconfig = MKTARGET $@
cmd_elfconfig = $(obj)/mk_elfconfig < $< > $@
$(obj)/elfconfig.h: $(obj)/empty.o $(obj)/mk_elfconfig FORCE
$(call if_changed,elfconfig)
targets += $(obj)/target.h
......@@ -199,7 +199,8 @@ cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $<
%.o: %.S FORCE
$(call if_changed_dep,as_o_S)
targets += $(real-objs-y) $(real-objs-m) $(EXTRA_TARGETS) $(MAKECMDGOALS)
targets += $(real-objs-y) $(real-objs-m) $(EXTRA_TARGETS) $(MAKECMDGOALS) \
$(build-targets)
# Build the compiled-in targets
# ---------------------------------------------------------------------------
......
#define KERNEL_ELFCLASS ELFCLASS32
#define KERNEL_ELFDATA ELFDATA2LSB
#define HOST_ELFCLASS ELFCLASS32
#define HOST_ELFDATA ELFDATA2LSB
/* empty file to figure out endianness / word size */
......@@ -14,13 +14,43 @@
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <endian.h>
/* 32 bits: if it turns out to be 64, we add explicitly (see EXTRA_SIZE). */
typedef int kernel_ulong_t;
#include "../include/linux/types.h"
#include "elfconfig.h"
/* We use the ELF typedefs, since we can't rely on stdint.h being present. */
#if KERNEL_ELFCLASS == ELFCLASS32
typedef Elf32_Addr kernel_ulong_t;
#else
typedef Elf64_Addr kernel_ulong_t;
#endif
typedef Elf32_Word __u32;
typedef Elf32_Half __u16;
typedef unsigned char __u8;
/* Big exception to the "don't include kernel headers into userspace, which
* even potentially has different endianness and word sizes, since
* we handle those differences explicitly below */
#include "../include/linux/mod_devicetable.h"
static int switch_endian;
#if KERNEL_ELFCLASS == ELFCLASS32
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#else
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#endif
#if KERNEL_ELFDATA != HOST_ELFDATA
static void __endian(const void *src, void *dest, unsigned int size)
{
......@@ -29,14 +59,22 @@ static void __endian(const void *src, void *dest, unsigned int size)
((unsigned char*)dest)[i] = ((unsigned char*)src)[size - i-1];
}
#define TO_NATIVE(x) \
({ \
typeof(x) __x; \
if (switch_endian) __endian(&(x), &(__x), sizeof(__x)); \
else __x = x; \
__endian(&(x), &(__x), sizeof(__x)); \
__x; \
})
#else /* endianness matches */
#define TO_NATIVE(x) (x)
#endif
#define ADD(str, sep, cond, field) \
do { \
strcat(str, sep); \
......@@ -161,30 +199,80 @@ static int do_table(void *symval, unsigned long size,
return wrote;
}
/* This is the best way of doing this without making a complete mess
of the code. */
#undef analyse_file
#undef Elf_Ehdr
#undef Elf_Shdr
#undef Elf_Sym
#undef EXTRA_SIZE
#define analyse_file analyze_file32
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define EXTRA_SIZE 0
#include "file2alias_inc.c"
#undef analyse_file
#undef Elf_Ehdr
#undef Elf_Shdr
#undef Elf_Sym
#undef EXTRA_SIZE
#define analyse_file analyze_file64
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define EXTRA_SIZE 4
#include "file2alias_inc.c"
/* This contains the cookie-cutter code for ELF handling (32 v 64). */
static void analyze_file(Elf_Ehdr *hdr,
unsigned int size,
const char *filename)
{
unsigned int i, num_syms = 0;
Elf_Shdr *sechdrs;
Elf_Sym *syms = NULL;
char *secstrings, *strtab = NULL;
int first = 1;
if (size < sizeof(*hdr))
goto truncated;
sechdrs = (void *)hdr + TO_NATIVE(hdr->e_shoff);
hdr->e_shoff = TO_NATIVE(hdr->e_shoff);
hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx);
hdr->e_shnum = TO_NATIVE(hdr->e_shnum);
for (i = 0; i < hdr->e_shnum; i++) {
sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type);
sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset);
sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size);
sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link);
}
/* Find symbol table. */
secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
for (i = 1; i < hdr->e_shnum; i++) {
if (sechdrs[i].sh_offset > size)
goto truncated;
if (sechdrs[i].sh_type == SHT_SYMTAB) {
syms = (void *)hdr + sechdrs[i].sh_offset;
num_syms = sechdrs[i].sh_size / sizeof(syms[0]);
} else if (sechdrs[i].sh_type == SHT_STRTAB)
strtab = (void *)hdr + sechdrs[i].sh_offset;
}
if (!strtab || !syms) {
fprintf(stderr, "table2alias: %s no symtab?\n", filename);
abort();
}
for (i = 0; i < num_syms; i++) {
const char *symname;
void *symval;
syms[i].st_shndx = TO_NATIVE(syms[i].st_shndx);
syms[i].st_name = TO_NATIVE(syms[i].st_name);
syms[i].st_value = TO_NATIVE(syms[i].st_value);
syms[i].st_size = TO_NATIVE(syms[i].st_size);
if (!syms[i].st_shndx || syms[i].st_shndx >= hdr->e_shnum)
continue;
symname = strtab + syms[i].st_name;
symval = (void *)hdr
+ sechdrs[syms[i].st_shndx].sh_offset
+ syms[i].st_value;
if (sym_is(symname, "__mod_pci_device_table"))
do_table(symval, syms[i].st_size,
sizeof(struct pci_device_id),
do_pci_entry, filename, &first);
else if (sym_is(symname, "__mod_usb_device_table"))
do_table(symval, syms[i].st_size,
sizeof(struct usb_device_id),
do_usb_entry, filename, &first);
}
return;
truncated:
fprintf(stderr, "table2alias: %s is truncated.\n", filename);
abort();
}
static void *grab_file(const char *filename, unsigned long *size)
{
......@@ -215,45 +303,16 @@ int main(int argc, char *argv[])
{
void *file;
unsigned long size;
int endian;
union { short s; char c[2]; } endian_test;
endian_test.s = 1;
if (endian_test.c[1] == 1) endian = ELFDATA2MSB;
else if (endian_test.c[0] == 1) endian = ELFDATA2LSB;
else
abort();
for (; argv[1]; argv++) {
file = grab_file(argv[1], &size);
if (!file) {
fprintf(stderr, "file2alias: opening %s: %s\n",
argv[1], strerror(errno));
continue;
}
if (size < SELFMAG || memcmp(file, ELFMAG, SELFMAG) != 0)
goto bad_elf;
if (((unsigned char *)file)[EI_DATA] != endian)
switch_endian = 1;
switch (((unsigned char *)file)[EI_CLASS]) {
case ELFCLASS32:
analyze_file32(file, size, argv[1]);
break;
case ELFCLASS64:
analyze_file64(file, size, argv[1]);
break;
default:
goto bad_elf;
abort();
}
analyze_file(file, size, argv[1]);
munmap(file, size);
continue;
bad_elf:
fprintf(stderr, "file2alias: %s is not elf\n", argv[1]);
return 1;
}
return 0;
}
/* This contains the cookie-cutter code for ELF handling (32 v 64).
Return true if anything output. */
static void analyse_file(Elf_Ehdr *hdr,
unsigned int size,
const char *filename)
{
unsigned int i, num_syms = 0;
Elf_Shdr *sechdrs;
Elf_Sym *syms = NULL;
char *secstrings, *strtab = NULL;
int first = 1;
if (size < sizeof(*hdr))
goto truncated;
sechdrs = (void *)hdr + TO_NATIVE(hdr->e_shoff);
if (switch_endian) {
hdr->e_shoff = TO_NATIVE(hdr->e_shoff);
hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx);
hdr->e_shnum = TO_NATIVE(hdr->e_shnum);
for (i = 0; i < hdr->e_shnum; i++) {
sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type);
sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset);
sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size);
sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link);
}
}
/* Find symbol table. */
secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
for (i = 1; i < hdr->e_shnum; i++) {
if (sechdrs[i].sh_offset > size)
goto truncated;
if (sechdrs[i].sh_type == SHT_SYMTAB) {
syms = (void *)hdr + sechdrs[i].sh_offset;
num_syms = sechdrs[i].sh_size / sizeof(syms[0]);
} else if (sechdrs[i].sh_type == SHT_STRTAB)
strtab = (void *)hdr + sechdrs[i].sh_offset;
}
if (!strtab || !syms) {
fprintf(stderr, "table2alias: %s no symtab?\n", filename);
return;
}
for (i = 0; i < num_syms; i++) {
const char *symname;
void *symval;
if (switch_endian) {
syms[i].st_shndx = TO_NATIVE(syms[i].st_shndx);
syms[i].st_name = TO_NATIVE(syms[i].st_name);
syms[i].st_value = TO_NATIVE(syms[i].st_value);
syms[i].st_size = TO_NATIVE(syms[i].st_size);
}
if (!syms[i].st_shndx || syms[i].st_shndx >= hdr->e_shnum)
continue;
symname = strtab + syms[i].st_name;
symval = (void *)hdr
+ sechdrs[syms[i].st_shndx].sh_offset
+ syms[i].st_value;
if (sym_is(symname, "__mod_pci_device_table"))
do_table(symval, syms[i].st_size,
sizeof(struct pci_device_id) + EXTRA_SIZE * 1,
do_pci_entry, filename, &first);
else if (sym_is(symname, "__mod_usb_device_table"))
do_table(symval, syms[i].st_size,
sizeof(struct usb_device_id) + EXTRA_SIZE * 1,
do_usb_entry, filename, &first);
}
return;
truncated:
fprintf(stderr, "table2alias: %s is truncated.\n", filename);
return;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <elf.h>
int
main(int argc, char **argv)
{
unsigned char ei[EI_NIDENT];
union { short s; char c[2]; } endian_test;
if (fread(ei, 1, EI_NIDENT, stdin) != EI_NIDENT) {
fprintf(stderr, "Error: input truncated\n");
return 1;
}
if (memcmp(ei, ELFMAG, SELFMAG) != 0) {
fprintf(stderr, "Error: not ELF\n");
return 1;
}
switch (ei[EI_CLASS]) {
case ELFCLASS32:
printf("#define KERNEL_ELFCLASS ELFCLASS32\n");
break;
case ELFCLASS64:
printf("#define KERNEL_ELFCLASS ELFCLASS64\n");
break;
default:
abort();
}
switch (ei[EI_DATA]) {
case ELFDATA2LSB:
printf("#define KERNEL_ELFDATA ELFDATA2LSB\n");
break;
case ELFDATA2MSB:
printf("#define KERNEL_ELFDATA ELFDATA2MSB\n");
break;
default:
abort();
}
if (sizeof(unsigned long) == 4) {
printf("#define HOST_ELFCLASS ELFCLASS32\n");
} else if (sizeof(unsigned long) == 8) {
printf("#define HOST_ELFCLASS ELFCLASS64\n");
}
endian_test.s = 0x0102;
if (memcmp(endian_test.c, "\x01\x02", 2) == 0)
printf("#define HOST_ELFDATA ELFDATA2MSB\n");
else if (memcmp(endian_test.c, "\x02\x01", 2) == 0)
printf("#define HOST_ELFDATA ELFDATA2LSB\n");
else
abort();
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment