ELF: add support for the ELF "merge" attribute

Add support for the "merge" attribute in ELF, along with the
associated "strings" and size specifier attributes.

Fix a few places where we used "int", but a larger type really ought
to have been used.

Be a bit more lax about respecifying attributes. For example, align=
can be respecified; the highest resulting value is used.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
H. Peter Anvin 2018-12-30 07:54:48 -08:00
parent 81f98fe79b
commit 88477764f3
4 changed files with 142 additions and 34 deletions

View File

@ -12,6 +12,9 @@ since 2007.
\b Suppress nuisance "\c{label changed during code generation}" messages
after a real error.
\b Add support for the \c{merge} and \c{strings} attributes on ELF
sections. See \k{elfsect}.
\S{cl-2.14.02} Version 2.14.02
\b Fix crash due to multiple errors or warnings during the code

View File

@ -256,9 +256,12 @@ Object File Format
\IA{sectalign}{sectalign}
\IR{solaris x86} Solaris x86
\IA{standard section names}{standardized section names}
\IR{strings, elf attribute} \c{strings}
\IR{symbols, exporting from dlls} symbols, exporting from DLLs
\IR{symbols, importing from dlls} symbols, importing from DLLs
\IR{test subdirectory} \c{test} subdirectory
\IR{thread local storage in elf} thread local storage, in \c{elf}
\IR{thread local storage in mach-o} thread local storage, in \c{macho}
\IR{tlink} \c{TLINK}
\IR{underscore, in c symbols} underscore, in C symbols
\IR{unicode} Unicode
@ -5951,6 +5954,26 @@ contents given, such as a BSS section.
\I{section alignment, in elf}\I{alignment, in elf sections}alignment
requirements of the section.
\b \c{ent=} or \c{entsize=} specifies the fundamental data item size
for a section which contains either fixed-sized data structures or
strings; this is generally used with the \c{merge} attribute (see
below.)
\b \c{byte}, \c{word}, \c{dword}, \c{qword}, \c{tword}, \c{oword},
\c{yword}, or \c{zword} are both shorthand for \c{entsize=}, but also
sets the default alignment.
\b \i{strings, ELF attribute}\c{strings} indicate that this section
contains exclusively null-terminated strings. By default these are
assumed to be byte strings, but a size specifier can be used to
override that.
\b \i\c{merge} indicates that duplicate data elements in this section
should be merged with data elements from other object files. Data
elements can be either fixed-sized objects or null-terminatedstrings
(with the \c{strings} attribute.) A size specifier is required unless
\c{strings} is specified, in which case the size defaults to \c{byte}.
\b \i\c{tls} defines the section to be one which contains
thread local variables.
@ -8213,7 +8236,7 @@ then the correct first instruction in the code section will not be
seen because the starting point skipped over it. This isn't really
ideal.
To avoid this, you can specify a `\i\c{synchronisation}' point, or indeed
To avoid this, you can specify a `\i{synchronisation}' point, or indeed
as many synchronisation points as you like (although NDISASM can
only handle 2147483647 sync points internally). The definition of a sync
point is this: NDISASM guarantees to hit sync points exactly during

View File

@ -1,6 +1,6 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 1996-2017 The NASM Authors - All Rights Reserved
*
* Copyright 1996-2018 The NASM Authors - All Rights Reserved
* See the file AUTHORS included with the NASM distribution for
* the specific copyright holders.
*
@ -14,7 +14,7 @@
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
@ -97,8 +97,10 @@ static int64_t elf_foffs;
static void elf_write(void);
static void elf_sect_write(struct elf_section *, const void *, size_t);
static void elf_sect_writeaddr(struct elf_section *, int64_t, size_t);
static void elf_section_header(int, int, uint64_t, void *, bool, uint64_t, int, int,
int, int);
static void elf_section_header(int name, int type, uint64_t flags,
void *data, bool is_saa, uint64_t datalen,
int link, int info,
uint64_t align, uint64_t entsize);
static void elf_write_sections(void);
static struct SAA *elf_build_symtab(int32_t *, int32_t *);
static struct SAA *elf_build_reltab(uint64_t *, struct elf_reloc *);
@ -211,9 +213,11 @@ const struct elf_known_section elf_known_sections[] = {
/* parse section attributes */
static void elf_section_attrib(char *name, char *attr, int pass,
uint32_t *flags_and, uint32_t *flags_or,
uint64_t *align, int *type)
uint64_t *alignp, uint64_t *entsize, int *type)
{
char *opt, *val, *next;
uint64_t align = 0;
uint64_t xalign = 0;
opt = nasm_skip_spaces(attr);
if (!opt || !*opt)
@ -225,14 +229,14 @@ static void elf_section_attrib(char *name, char *attr, int pass,
nasm_error(ERR_NONFATAL,
"section align without value specified");
} else {
*align = atoi(val);
if (*align == 0) {
*align = SHA_ANY;
} else if (!is_power2(*align)) {
bool err;
uint64_t a = readnum(val, &err);
if (a && !is_power2(a)) {
nasm_error(ERR_NONFATAL,
"section alignment %"PRId64" is not a power of two",
*align);
*align = SHA_ANY;
a);
} else if (a > align) {
align = a;
}
}
} else if (!nasm_stricmp(opt, "alloc")) {
@ -250,16 +254,64 @@ static void elf_section_attrib(char *name, char *attr, int pass,
} else if (!nasm_stricmp(opt, "write")) {
*flags_and |= SHF_WRITE;
*flags_or |= SHF_WRITE;
} else if (!nasm_stricmp(opt, "tls")) {
*flags_and |= SHF_TLS;
*flags_or |= SHF_TLS;
} else if (!nasm_stricmp(opt, "nowrite")) {
*flags_and |= SHF_WRITE;
*flags_or &= ~SHF_WRITE;
} else if (!nasm_stricmp(opt, "tls")) {
*flags_and |= SHF_TLS;
*flags_or |= SHF_TLS;
} else if (!nasm_stricmp(opt, "notls")) {
*flags_and |= SHF_TLS;
*flags_or &= ~SHF_TLS;
} else if (!nasm_stricmp(opt, "merge")) {
*flags_and |= SHF_MERGE;
*flags_or |= SHF_MERGE;
} else if (!nasm_stricmp(opt, "nomerge")) {
*flags_and |= SHF_MERGE;
*flags_or &= ~SHF_MERGE;
} else if (!nasm_stricmp(opt, "strings")) {
*flags_and |= SHF_STRINGS;
*flags_or |= SHF_STRINGS;
} else if (!nasm_stricmp(opt, "nostrings")) {
*flags_and |= SHF_STRINGS;
*flags_or &= ~SHF_STRINGS;
} else if (!nasm_stricmp(opt, "progbits")) {
*type = SHT_PROGBITS;
} else if (!nasm_stricmp(opt, "nobits")) {
*type = SHT_NOBITS;
} else if (!nasm_stricmp(opt, "ent") || !nasm_stricmp(opt,"entsize")) {
bool err;
uint64_t es;
if (!val) {
nasm_error(ERR_NONFATAL,
"section attribute %s without value specified", opt);
} else {
es = readnum(val, &err);
if (err) {
nasm_error(ERR_NONFATAL,
"invalid value %s for section attribute %s",
val, opt);
} else {
*entsize = es;
}
}
} else if (!nasm_stricmp(opt, "byte")) {
xalign = *entsize = 1;
} else if (!nasm_stricmp(opt, "word")) {
xalign = *entsize = 2;
} else if (!nasm_stricmp(opt, "dword")) {
xalign = *entsize = 4;
} else if (!nasm_stricmp(opt, "qword")) {
xalign = *entsize = 8;
} else if (!nasm_stricmp(opt, "tword")) {
*entsize = 10;
xalign = 2;
} else if (!nasm_stricmp(opt, "oword")) {
xalign = *entsize = 16;
} else if (!nasm_stricmp(opt, "yword")) {
xalign = *entsize = 32;
} else if (!nasm_stricmp(opt, "zword")) {
xalign = *entsize = 64;
} else if (pass == 1) {
nasm_error(ERR_WARNING,
"Unknown section attribute '%s' ignored on"
@ -267,6 +319,14 @@ static void elf_section_attrib(char *name, char *attr, int pass,
}
opt = next;
}
if (!align)
align = xalign;
if (!align)
align = SHA_ANY;
*alignp = align;
}
static enum directive_result
@ -389,7 +449,7 @@ static void add_sectname(const char *firsthalf, const char *secondhalf)
shstrtablen += len + 1;
}
static int elf_make_section(char *name, int type, int flags, int align)
static int elf_make_section(char *name, int type, int flags, uint64_t align)
{
struct elf_section *s;
@ -420,7 +480,8 @@ static int32_t elf_section_names(char *name, int pass, int *bits)
{
char *p;
uint32_t flags, flags_and, flags_or;
uint64_t align;
uint64_t align, entsize;
struct elf_section *s;
int type, i;
if (!name) {
@ -431,10 +492,10 @@ static int32_t elf_section_names(char *name, int pass, int *bits)
p = nasm_skip_word(name);
if (*p)
*p++ = '\0';
flags_and = flags_or = type = align = 0;
flags_and = flags_or = type = align = entsize = 0;
elf_section_attrib(name, p, pass, &flags_and,
&flags_or, &align, &type);
&flags_or, &align, &entsize, &type);
if (!strcmp(name, ".shstrtab") ||
!strcmp(name, ".symtab") ||
@ -461,15 +522,34 @@ static int32_t elf_section_names(char *name, int pass, int *bits)
flags = (ks->flags & ~flags_and) | flags_or;
i = elf_make_section(name, type, flags, align);
} else if (pass == 1) {
if ((type && sects[i]->type != type)
|| (align && sects[i]->align != align)
|| (flags_and && ((sects[i]->flags & flags_and) != flags_or)))
nasm_error(ERR_WARNING, "incompatible section attributes ignored on"
" redeclaration of section `%s'", name);
}
return sects[i]->index;
s = sects[i];
if (pass == 1) {
if ((type && s->type != type)
|| ((s->flags & flags_and) != flags_or)
|| (entsize && s->entsize && entsize != s->entsize)) {
nasm_error(ERR_WARNING,
"incompatible section attributes ignored on"
" redeclaration of section `%s'", name);
}
}
if (align > s->align)
s->align = align;
if (entsize && !s->entsize)
s->entsize = entsize;
if (pass == 2 && (flags_or & SHF_MERGE) && s->entsize == 0) {
if (!(s->flags & SHF_STRINGS))
nasm_error(ERR_NONFATAL,
"section attribute merge specified without an entry size");
s->entsize = 1;
}
return s->index;
}
static void elf_deflabel(char *name, int32_t segment, int64_t offset,
@ -868,7 +948,7 @@ static void elf32_out(int32_t segto, const void *data,
" segment base references");
} else {
if (wrt == NO_SEG) {
/*
/*
* The if() is a hack to deal with compilers which
* don't handle switch() statements with 64-bit
* expressions.
@ -1693,9 +1773,9 @@ static void elf_write(void)
/* The normal sections */
for (i = 0; i < nsects; i++) {
elf_section_header(p - shstrtab, sects[i]->type, sects[i]->flags,
(sects[i]->type == SHT_PROGBITS ?
sects[i]->data : NULL), true,
sects[i]->len, 0, 0, sects[i]->align, 0);
sects[i]->data, true,
sects[i]->len, 0, 0,
sects[i]->align, sects[i]->entsize);
p += strlen(p) + 1;
}
@ -2129,7 +2209,8 @@ static struct SAA *elf_build_reltab(uint64_t *len, struct elf_reloc *r)
static void elf_section_header(int name, int type, uint64_t flags,
void *data, bool is_saa, uint64_t datalen,
int link, int info, int align, int eltsize)
int link, int info,
uint64_t align, uint64_t entsize)
{
union {
Elf32_Shdr shdr32;
@ -2153,7 +2234,7 @@ static void elf_section_header(int name, int type, uint64_t flags,
shdr.shdr32.sh_link = cpu_to_le32(link);
shdr.shdr32.sh_info = cpu_to_le32(info);
shdr.shdr32.sh_addralign = cpu_to_le32(align);
shdr.shdr32.sh_entsize = cpu_to_le32(eltsize);
shdr.shdr32.sh_entsize = cpu_to_le32(entsize);
} else {
nasm_assert(is_elf64());
@ -2168,7 +2249,7 @@ static void elf_section_header(int name, int type, uint64_t flags,
shdr.shdr64.sh_link = cpu_to_le32(link);
shdr.shdr64.sh_info = cpu_to_le32(info);
shdr.shdr64.sh_addralign = cpu_to_le64(align);
shdr.shdr64.sh_entsize = cpu_to_le64(eltsize);
shdr.shdr64.sh_entsize = cpu_to_le64(entsize);
}
nasm_write(&shdr, is_elf64() ? sizeof(shdr.shdr64) : sizeof(shdr.shdr32), ofile);

View File

@ -141,6 +141,7 @@ struct elf_section {
int type; /* SHT_PROGBITS or SHT_NOBITS */
uint64_t align; /* alignment: power of two */
uint64_t flags; /* section flags */
uint64_t entsize; /* entry size */
char *name;
struct SAA *rel;
uint64_t rellen;