aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2019-09-12 14:27:50 -0400
committerH. Peter Anvin <hpa@zytor.com>2019-09-12 14:27:50 -0400
commit495fda63418600229f36a3a7de62b75620be34b6 (patch)
tree3ca7b96fb4f9c73674228ea7cb3445b35ef4302c
parenta73ccfebcc9f60e6f2234cbf10cf7551279524d3 (diff)
downloadnasm-495fda63418600229f36a3a7de62b75620be34b6.tar.gz
nasm-495fda63418600229f36a3a7de62b75620be34b6.tar.xz
nasm-495fda63418600229f36a3a7de62b75620be34b6.zip
elf: support weak global and extern references
A global or extern definition can now contain the keyword "weak" (or "strong", although that is the default) to create a weak symbol or a weak external reference, respectively. Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
-rw-r--r--output/outelf.c225
-rw-r--r--output/outelf.h4
-rw-r--r--test/elf_visibility.asm23
3 files changed, 155 insertions, 97 deletions
diff --git a/output/outelf.c b/output/outelf.c
index 948636a6..0c889bb6 100644
--- a/output/outelf.c
+++ b/output/outelf.c
@@ -727,12 +727,19 @@ static int32_t elf_section_names(char *name, int *bits)
return s->index;
}
+static inline bool sym_type_local(int type)
+{
+ return ELF32_ST_BIND(type) == STB_LOCAL;
+}
+
static void elf_deflabel(char *name, int32_t segment, int64_t offset,
int is_global, char *special)
{
int pos = strslen;
struct elf_symbol *sym;
- bool special_used = false;
+ const char *spcword = nasm_skip_spaces(special);
+ int bind, type; /* st_info components */
+ const struct elf_section *sec = NULL;
if (debug_level(2)) {
nasm_debug(" elf_deflabel: %s, seg=%"PRIx32", off=%"PRIx64", is_global=%d, %s\n",
@@ -797,13 +804,13 @@ static void elf_deflabel(char *name, int32_t segment, int64_t offset,
memset(&sym->symv, 0, sizeof(struct rbtree));
sym->strpos = pos;
- sym->type = is_global ? SYM_GLOBAL : SYM_LOCAL;
+ bind = is_global ? STB_GLOBAL : STB_LOCAL;
+ type = STT_NOTYPE;
sym->other = STV_DEFAULT;
sym->size = 0;
- if (segment == NO_SEG)
+ if (segment == NO_SEG) {
sym->section = XSHN_ABS;
- else {
- const struct elf_section *s;
+ } else {
sym->section = XSHN_UNDEF;
if (segment == def_seg) {
/* we have to be sure at least text section is there */
@@ -811,9 +818,9 @@ static void elf_deflabel(char *name, int32_t segment, int64_t offset,
if (segment != elf_section_names(".text", &tempint))
nasm_panic("strange segment conditions in ELF driver");
}
- s = raa_read_ptr(section_by_index, segment >> 1);
- if (s)
- sym->section = s->shndx;
+ sec = raa_read_ptr(section_by_index, segment >> 1);
+ if (sec)
+ sym->section = sec->shndx;
}
if (is_global == 2) {
@@ -824,22 +831,133 @@ static void elf_deflabel(char *name, int32_t segment, int64_t offset,
* We have a common variable. Check the special text to see
* if it's a valid number and power of two; if so, store it
* as the alignment for the common variable.
+ *
+ * XXX: this should allow an expression.
*/
- if (special) {
+ if (spcword) {
bool err;
- sym->symv.key = readnum(special, &err);
+ sym->symv.key = readnum(spcword, &err);
if (err)
nasm_nonfatal("alignment constraint `%s' is not a"
" valid number", special);
- else if ((sym->symv.key | (sym->symv.key - 1)) != 2 * sym->symv.key - 1)
+ else if (!is_power2(sym->symv.key))
nasm_nonfatal("alignment constraint `%s' is not a"
" power of two", special);
+ spcword = nasm_skip_spaces(nasm_skip_word(spcword));
}
- special_used = true;
- } else
+ } else {
sym->symv.key = (sym->section == XSHN_UNDEF ? 0 : offset);
+ }
+
+ if (spcword && *spcword) {
+ const char *wend;
+ bool ok = true;
+
+ while (ok) {
+ size_t wlen;
+ wend = nasm_skip_word(spcword);
+ wlen = wend - spcword;
+
+ switch (wlen) {
+ case 4:
+ if (!nasm_strnicmp(spcword, "data", wlen))
+ type = STT_OBJECT;
+ else if (!nasm_strnicmp(spcword, "weak", wlen))
+ bind = STB_WEAK;
+ else
+ ok = false;
+ break;
+
+ case 6:
+ if (!nasm_strnicmp(spcword, "notype", wlen))
+ type = STT_NOTYPE;
+ else if (!nasm_strnicmp(spcword, "object", wlen))
+ type = STT_NOTYPE;
+ else if (!nasm_strnicmp(spcword, "hidden", wlen))
+ sym->other = STV_HIDDEN;
+ else if (!nasm_strnicmp(spcword, "strong", wlen))
+ bind = STB_GLOBAL;
+ else
+ ok = false;
+ break;
+
+ case 7:
+ if (!nasm_strnicmp(spcword, "default", wlen))
+ sym->other = STV_DEFAULT;
+ else
+ ok = false;
+ break;
+
+ case 8:
+ if (!nasm_strnicmp(spcword, "function", wlen))
+ type = STT_FUNC;
+ else if (!nasm_stricmp(spcword, "internal"))
+ sym->other = STV_INTERNAL;
+ else
+ ok = false;
+ break;
+
+ case 9:
+ if (!nasm_strnicmp(spcword, "protected", wlen))
+ sym->other = STV_PROTECTED;
+ else
+ ok = false;
+ break;
+
+ default:
+ ok = false;
+ break;
+ }
+
+ if (ok)
+ spcword = nasm_skip_spaces(wend);
+ }
+ if (!is_global && bind != STB_LOCAL) {
+ nasm_nonfatal("weak and strong only applies to global variables");
+ bind = STB_LOCAL;
+ }
+
+ if (spcword && *spcword) {
+ struct tokenval tokval;
+ expr *e;
+ int fwd = 0;
+ char *saveme = stdscan_get();
+
+ /*
+ * We have a size expression; attempt to
+ * evaluate it.
+ */
+ stdscan_reset();
+ stdscan_set((char *)spcword);
+ tokval.t_type = TOKEN_INVALID;
+ e = evaluate(stdscan, NULL, &tokval, &fwd, 0, NULL);
+ if (fwd) {
+ sym->nextfwd = fwds;
+ fwds = sym;
+ sym->name = nasm_strdup(name);
+ } else if (e) {
+ if (!is_simple(e))
+ nasm_nonfatal("cannot use relocatable"
+ " expression as symbol size");
+ else
+ sym->size = reloc_value(e);
+ }
+ stdscan_set(saveme);
+ }
+ }
+
+ /*
+ * If it is in a TLS segment, mark symbol accordingly.
+ */
+ if (sec && (sec->flags & SHF_TLS))
+ type = STT_TLS;
- if (sym->type == SYM_GLOBAL) {
+ /* Note: ELF32_ST_INFO() and ELF64_ST_INFO() are identical */
+ sym->type = ELF32_ST_INFO(bind, type);
+
+ if (sym_type_local(sym->type)) {
+ nlocals++;
+ } else {
/*
* If sym->section == SHN_ABS, then the first line of the
* else section would cause a core dump, because its a reference
@@ -863,83 +981,10 @@ static void elf_deflabel(char *name, int32_t segment, int64_t offset,
sects[sym->section-1]->gsyms =
rb_insert(sects[sym->section-1]->gsyms, &sym->symv);
- if (special) {
- int n = strcspn(special, " \t");
-
- if (!nasm_strnicmp(special, "function", n))
- sym->type |= STT_FUNC;
- else if (!nasm_strnicmp(special, "data", n) ||
- !nasm_strnicmp(special, "object", n))
- sym->type |= STT_OBJECT;
- else if (!nasm_strnicmp(special, "notype", n))
- sym->type |= STT_NOTYPE;
- else
- nasm_nonfatal("unrecognised symbol type `%.*s'",
- n, special);
- special += n;
-
- special = nasm_skip_spaces(special);
- if (*special) {
- n = strcspn(special, " \t");
- if (!nasm_strnicmp(special, "default", n))
- sym->other = STV_DEFAULT;
- else if (!nasm_strnicmp(special, "internal", n))
- sym->other = STV_INTERNAL;
- else if (!nasm_strnicmp(special, "hidden", n))
- sym->other = STV_HIDDEN;
- else if (!nasm_strnicmp(special, "protected", n))
- sym->other = STV_PROTECTED;
- else
- n = 0;
- special += n;
- }
-
- if (*special) {
- struct tokenval tokval;
- expr *e;
- int fwd = 0;
- char *saveme = stdscan_get();
-
- while (special[n] && nasm_isspace(special[n]))
- n++;
- /*
- * We have a size expression; attempt to
- * evaluate it.
- */
- stdscan_reset();
- stdscan_set(special + n);
- tokval.t_type = TOKEN_INVALID;
- e = evaluate(stdscan, NULL, &tokval, &fwd, 0, NULL);
- if (fwd) {
- sym->nextfwd = fwds;
- fwds = sym;
- sym->name = nasm_strdup(name);
- } else if (e) {
- if (!is_simple(e))
- nasm_nonfatal("cannot use relocatable"
- " expression as symbol size");
- else
- sym->size = reloc_value(e);
- }
- stdscan_set(saveme);
- }
- special_used = true;
- }
- /*
- * If TLS segment, mark symbol accordingly.
- */
- if (sects[sym->section - 1]->flags & SHF_TLS) {
- sym->type &= 0xf0;
- sym->type |= STT_TLS;
- }
}
sym->globnum = nglobs;
nglobs++;
- } else
- nlocals++;
-
- if (special && !special_used)
- nasm_nonfatal("no special symbol features supported here");
+ }
}
static void elf_add_reloc(struct elf_section *sect, int32_t segment,
@@ -2133,7 +2178,7 @@ static size_t elf_build_symtab(void)
*/
saa_rewind(syms);
while ((sym = saa_rstruct(syms))) {
- if (sym->type & SYM_GLOBAL)
+ if (!sym_type_local(sym->type))
continue;
elf_sym(sym);
@@ -2146,7 +2191,7 @@ static size_t elf_build_symtab(void)
*/
saa_rewind(syms);
while ((sym = saa_rstruct(syms))) {
- if (!(sym->type & SYM_GLOBAL))
+ if (sym_type_local(sym->type))
continue;
elf_sym(sym);
diff --git a/output/outelf.h b/output/outelf.h
index f5ccfe1c..7443dffc 100644
--- a/output/outelf.h
+++ b/output/outelf.h
@@ -41,10 +41,6 @@
#include "rbtree.h"
#include "saa.h"
-/* symbol binding */
-#define SYM_GLOBAL ELF32_ST_MKBIND(STB_GLOBAL)
-#define SYM_LOCAL ELF32_ST_MKBIND(STB_LOCAL)
-
#define GLOBAL_TEMP_BASE 0x40000000 /* bigger than any sane symbol index */
/* alignment of sections in file */
diff --git a/test/elf_visibility.asm b/test/elf_visibility.asm
index 4874b62d..8098e227 100644
--- a/test/elf_visibility.asm
+++ b/test/elf_visibility.asm
@@ -1,10 +1,27 @@
-global foo
+global foo:(foo_end - foo)
global foo_hidden:function hidden
+global foo_protected:function protected
+global foo_internal:function internal
+global foo_weak:function weak
+global foo_hidden_weak:function hidden weak
+
+extern strong_ref, weak_ref:weak
SECTION .text align=16
foo:
+ nop
foo_hidden:
+ nop
+foo_protected:
+ nop
+foo_internal:
+ nop
+foo_weak:
+ ret
+foo_hidden_weak:
+ mov eax,weak_ref
+ mov eax,strong_ref
foo_label:
-ret
-
+ ret
+foo_end: