aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2019-09-12 20:21:03 -0400
committerH. Peter Anvin <hpa@zytor.com>2019-09-12 20:21:03 -0400
commit90b1ccff86d530b140eb391ede3c50e33bcf9410 (patch)
tree1ef248693a5d5b8c226a99f2626734f4f76f88c5
parent495fda63418600229f36a3a7de62b75620be34b6 (diff)
downloadnasm-90b1ccff86d530b140eb391ede3c50e33bcf9410.tar.gz
nasm-90b1ccff86d530b140eb391ede3c50e33bcf9410.tar.xz
nasm-90b1ccff86d530b140eb391ede3c50e33bcf9410.zip
Drop unnecessary EXTERN symbols
Currently, NASM always issues as an unknown symbol any symbol declared EXTERN. This is highly undesirable when using common header files, as it might cause the linker to pull in a bunch of unnecessary modules, depending on how smart the linker is. Add a new REQUIRED directive which behaves like the old EXTERN, for the use cases which might still need this behavior. Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
-rw-r--r--asm/directiv.c5
-rw-r--r--asm/directiv.dat4
-rw-r--r--asm/eval.c8
-rw-r--r--asm/labels.c79
-rw-r--r--include/labels.h13
-rw-r--r--macros/standard.mac7
-rw-r--r--output/outbin.c6
-rw-r--r--output/outmacho.c2
-rw-r--r--test/elf_visibility.asm3
9 files changed, 81 insertions, 46 deletions
diff --git a/asm/directiv.c b/asm/directiv.c
index 22e6a624..96464d62 100644
--- a/asm/directiv.c
+++ b/asm/directiv.c
@@ -297,6 +297,9 @@ bool process_directives(char *directive)
case D_EXTERN:
type = LBL_EXTERN;
goto symdef;
+ case D_REQUIRED:
+ type = LBL_REQUIRED;
+ goto symdef;
case D_COMMON:
type = LBL_COMMON;
goto symdef;
@@ -356,7 +359,7 @@ bool process_directives(char *directive)
if (!declare_label(value, type, special))
break;
- if (type == LBL_COMMON || type == LBL_EXTERN)
+ if (type == LBL_COMMON || type == LBL_EXTERN || type == LBL_REQUIRED)
define_label(value, 0, size, false);
break;
diff --git a/asm/directiv.dat b/asm/directiv.dat
index 4b0ca3f0..5659ee14 100644
--- a/asm/directiv.dat
+++ b/asm/directiv.dat
@@ -44,6 +44,9 @@
;; same D_ constant will be used for both, and this is perfectly
;; acceptable.
;;
+;; In the future, this will be turned into a general list of keywords
+;; to be parsed in special contexts.
+;;
; --- General configuration
#name directive
@@ -73,6 +76,7 @@ segment
warning
sectalign
pragma
+required
; --- Format-specific directives
export ; outcoff, outobj
diff --git a/asm/eval.c b/asm/eval.c
index 9ec093a0..db915ef2 100644
--- a/asm/eval.c
+++ b/asm/eval.c
@@ -972,7 +972,9 @@ static expr *expr6(void)
label_seg = in_absolute ? absolute.segment : location.segment;
label_ofs = in_absolute ? absolute.offset : location.offset;
} else {
- if (!lookup_label(tokval->t_charptr, &label_seg, &label_ofs)) {
+ enum label_type ltype;
+ ltype = lookup_label(tokval->t_charptr, &label_seg, &label_ofs);
+ if (ltype == LBL_NONE) {
scope = local_scope(tokval->t_charptr);
if (critical) {
nasm_nonfatal("symbol `%s%s' not defined%s",
@@ -985,9 +987,9 @@ static expr *expr6(void)
type = EXPR_UNKNOWN;
label_seg = NO_SEG;
label_ofs = 1;
- }
- if (opflags && is_extern(tokval->t_charptr))
+ } else if (is_extern(ltype)) {
*opflags |= OPFLAG_EXTERN;
+ }
}
addtotemp(type, label_ofs);
if (label_seg != NO_SEG)
diff --git a/asm/labels.c b/asm/labels.c
index f973d7ed..4618fc70 100644
--- a/asm/labels.c
+++ b/asm/labels.c
@@ -96,9 +96,10 @@ static bool set_prevlabel(const char *l)
#endif
/* string values for enum label_type */
-static const char * const types[] =
-{"local", "global", "static", "extern", "common", "special",
- "output format special"};
+static const char * const types[] = {
+ "local", "static", "global", "extern", "required", "common",
+ "special", "output format special"
+};
union label { /* actual label structures */
struct {
@@ -107,6 +108,7 @@ union label { /* actual label structures */
int64_t offset;
int64_t size;
int64_t defined; /* 0 if undefined, passn+1 for when defn seen */
+ int64_t lastref; /* Last pass where we saw a reference */
char *label, *mangled, *special;
const char *def_file; /* Where defined */
int32_t def_line;
@@ -157,7 +159,7 @@ static void out_symdef(union label *lptr)
/* Emit special fixups for globals and commons */
switch (lptr->defn.type) {
case LBL_GLOBAL:
- case LBL_EXTERN:
+ case LBL_REQUIRED:
case LBL_COMMON:
if (lptr->defn.special)
ofmt->symdef(lptr->defn.mangled, 0, 0, 3, lptr->defn.special);
@@ -173,8 +175,17 @@ static void out_symdef(union label *lptr)
/* Clean up this hack... */
switch(lptr->defn.type) {
- case LBL_GLOBAL:
case LBL_EXTERN:
+ /* If not seen in the previous or this pass, drop it */
+ if (lptr->defn.lastref < pass_count())
+ return;
+
+ /* Otherwise, promote to LBL_REQUIRED at this time */
+ lptr->defn.type = LBL_REQUIRED;
+
+ /* fall through */
+ case LBL_GLOBAL:
+ case LBL_REQUIRED:
backend_type = 1;
backend_offset = lptr->defn.offset;
break;
@@ -255,32 +266,30 @@ static union label *find_label(const char *label, bool create, bool *created)
return lfree++;
}
-bool lookup_label(const char *label, int32_t *segment, int64_t *offset)
+enum label_type lookup_label(const char *label,
+ int32_t *segment, int64_t *offset)
{
union label *lptr;
if (!initialized)
- return false;
+ return LBL_NONE;
lptr = find_label(label, false, NULL);
if (lptr && lptr->defn.defined) {
+ int64_t lpass = pass_count() + 1;
+
+ lptr->defn.lastref = lpass;
*segment = lptr->defn.segment;
*offset = lptr->defn.offset;
- return true;
+ return lptr->defn.type;
}
- return false;
+ return LBL_NONE;
}
-bool is_extern(const char *label)
+static inline bool is_global(enum label_type type)
{
- union label *lptr;
-
- if (!initialized)
- return false;
-
- lptr = find_label(label, false, NULL);
- return lptr && lptr->defn.type == LBL_EXTERN;
+ return type == LBL_GLOBAL || type == LBL_COMMON;
}
static const char *mangle_strings[] = {"", "", "", ""};
@@ -314,6 +323,7 @@ static const char *mangle_label_name(union label *lptr)
case LBL_GLOBAL:
case LBL_STATIC:
case LBL_EXTERN:
+ case LBL_REQUIRED:
prefix = mangle_strings[LM_GPREFIX];
suffix = mangle_strings[LM_GSUFFIX];
break;
@@ -376,12 +386,15 @@ handle_herelabel(union label *lptr, int32_t *segment, int64_t *offset)
static bool declare_label_lptr(union label *lptr,
enum label_type type, const char *special)
{
+ enum label_type oldtype = lptr->defn.type;
+
if (special && !special[0])
special = NULL;
- if (lptr->defn.type == type ||
- (!pass_stable() && lptr->defn.type == LBL_LOCAL)) {
+ if (oldtype == type || (!pass_stable() && oldtype == LBL_LOCAL) ||
+ (oldtype == LBL_EXTERN && type == LBL_REQUIRED)) {
lptr->defn.type = type;
+
if (special) {
if (!lptr->defn.special)
lptr->defn.special = perm_copy(special);
@@ -390,29 +403,29 @@ static bool declare_label_lptr(union label *lptr,
lptr->defn.label, lptr->defn.special, special);
}
return true;
- }
-
- /* EXTERN can be replaced with GLOBAL or COMMON */
- if (lptr->defn.type == LBL_EXTERN &&
- (type == LBL_GLOBAL || type == LBL_COMMON)) {
+ } else if (is_extern(oldtype) && is_global(type)) {
+ /* EXTERN or REQUIRED can be replaced with GLOBAL or COMMON */
lptr->defn.type = type;
+
/* Override special unconditionally */
if (special)
lptr->defn.special = perm_copy(special);
return true;
- }
+ } else if (is_extern(type) && (is_global(oldtype) || is_extern(oldtype))) {
+ /*
+ * GLOBAL or COMMON ignore subsequent EXTERN or REQUIRED;
+ * REQUIRED ignores subsequent EXTERN.
+ */
- /* GLOBAL or COMMON ignore subsequent EXTERN */
- if ((lptr->defn.type == LBL_GLOBAL || lptr->defn.type == LBL_COMMON) &&
- type == LBL_EXTERN) {
+ /* Ignore special unless we don't already have one */
if (!lptr->defn.special)
lptr->defn.special = perm_copy(special);
- return false; /* Don't call define_label() after this! */
+
+ return false; /* Don't call define_label() after this! */
}
nasm_nonfatal("symbol `%s' declared both as %s and %s",
lptr->defn.label, types[lptr->defn.type], types[type]);
-
return false;
}
@@ -452,13 +465,13 @@ void define_label(const char *label, int32_t segment,
if (segment) {
/* We are actually defining this label */
- if (lptr->defn.type == LBL_EXTERN) {
- /* auto-promote EXTERN to GLOBAL */
+ if (is_extern(lptr->defn.type)) {
+ /* auto-promote EXTERN/REQUIRED to GLOBAL */
lptr->defn.type = LBL_GLOBAL;
lastdef = 0; /* We are "re-creating" this label */
}
} else {
- /* It's a pseudo-segment (extern, common) */
+ /* It's a pseudo-segment (extern, required, common) */
segment = lptr->defn.segment ? lptr->defn.segment : seg_alloc();
}
diff --git a/include/labels.h b/include/labels.h
index 9cf57c1b..32df8071 100644
--- a/include/labels.h
+++ b/include/labels.h
@@ -48,17 +48,22 @@ enum mangle_index {
};
enum label_type {
- LBL_LOCAL, /* Must be zero */
- LBL_GLOBAL,
+ LBL_NONE = -1, /* No label */
+ LBL_LOCAL = 0, /* Must be zero */
LBL_STATIC,
+ LBL_GLOBAL,
LBL_EXTERN,
+ LBL_REQUIRED, /* Like extern but emit even if unused */
LBL_COMMON,
LBL_SPECIAL, /* Magic symbols like ..start */
LBL_BACKEND /* Backend-defined symbols like ..got */
};
-bool lookup_label(const char *label, int32_t *segment, int64_t *offset);
-bool is_extern(const char *label);
+enum label_type lookup_label(const char *label, int32_t *segment, int64_t *offset);
+static inline bool is_extern(enum label_type type)
+{
+ return type == LBL_EXTERN || type == LBL_REQUIRED;
+}
void define_label(const char *label, int32_t segment, int64_t offset,
bool normal);
void backend_label(const char *label, int32_t segment, int64_t offset);
diff --git a/macros/standard.mac b/macros/standard.mac
index f6ca65d2..a2ab4fc1 100644
--- a/macros/standard.mac
+++ b/macros/standard.mac
@@ -166,6 +166,13 @@ STD: nasm
%endrep
%endmacro
+%imacro required 1-*.nolist
+ %rep %0
+ [required %1]
+ %rotate 1
+ %endrep
+%endmacro
+
%imacro common 1-*.nolist
%rep %0
[common %1]
diff --git a/output/outbin.c b/output/outbin.c
index 8baa648a..5c2f0631 100644
--- a/output/outbin.c
+++ b/output/outbin.c
@@ -642,7 +642,7 @@ static void bin_cleanup(void)
if (map_control & MAP_SYMBOLS) {
int32_t segment;
int64_t offset;
- bool found_label;
+ enum label_type found_label;
fprintf(rf, "-- Symbols ");
for (h = 68; h; h--)
@@ -655,7 +655,7 @@ static void bin_cleanup(void)
fprintf(rf, "\n\nValue Name\n");
list_for_each(l, no_seg_labels) {
found_label = lookup_label(l->name, &segment, &offset);
- nasm_assert(found_label);
+ nasm_assert(found_label != LBL_NONE);
fprintf(rf, "%08"PRIX64" %s\n", offset, l->name);
}
fprintf(rf, "\n\n");
@@ -668,7 +668,7 @@ static void bin_cleanup(void)
fprintf(rf, "\n\nReal Virtual Name\n");
list_for_each(l, s->labels) {
found_label = lookup_label(l->name, &segment, &offset);
- nasm_assert(found_label);
+ nasm_assert(found_label != LBL_NONE);
fprintf(rf, "%16"PRIX64" %16"PRIX64" %s\n",
s->start + offset, s->vstart + offset,
l->name);
diff --git a/output/outmacho.c b/output/outmacho.c
index 5ad6601d..6cbaa012 100644
--- a/output/outmacho.c
+++ b/output/outmacho.c
@@ -1740,7 +1740,7 @@ static bool macho_set_section_attribute_by_symbol(const char *label, uint32_t fl
int32_t nasm_seg;
int64_t offset;
- if (!lookup_label(label, &nasm_seg, &offset)) {
+ if (lookup_label(label, &nasm_seg, &offset) == LBL_NONE) {
nasm_error(ERR_NONFATAL, "unknown symbol `%s' in no_dead_strip", label);
return false;
}
diff --git a/test/elf_visibility.asm b/test/elf_visibility.asm
index 8098e227..81ea7a0c 100644
--- a/test/elf_visibility.asm
+++ b/test/elf_visibility.asm
@@ -5,7 +5,8 @@ global foo_internal:function internal
global foo_weak:function weak
global foo_hidden_weak:function hidden weak
-extern strong_ref, weak_ref:weak
+extern strong_ref, weak_ref:weak, unused_ref
+required required_ref
SECTION .text align=16