[nasm:master] output/coff: Support for COMDAT sections

nasm-bot for Igor Glucksmann 33635651+igg0 at users.noreply.github.com
Mon Nov 7 17:12:20 PST 2022


Commit-ID:  ed2c609976826ce087731b15d70eef2ff64d7a1a
Gitweb:     http://repo.or.cz/w/nasm.git?a=commitdiff;h=ed2c609976826ce087731b15d70eef2ff64d7a1a
Author:     Igor Glucksmann <33635651+igg0 at users.noreply.github.com>
AuthorDate: Wed, 31 Mar 2021 15:23:11 +0200
Committer:  Cyrill Gorcunov <gorcunov at gmail.com>
CommitDate: Fri, 17 Dec 2021 23:45:25 +0300

output/coff: Support for COMDAT sections

Signed-off-by: "Glücksmann, Igor" <igor.glucksmann at avast.com>
Signed-off-by: Cyrill Gorcunov <gorcunov at gmail.com>


---
 Makefile.in                     |   2 +-
 Mkfiles/msvc.mak                |   2 +-
 Mkfiles/openwcom.mak            |   2 +-
 include/hashtbl.h               |   2 +
 rdoff/hash.c => nasmlib/crc32.c |  29 +++----
 output/outcoff.c                | 168 +++++++++++++++++++++++++++++++++++-----
 output/pecoff.h                 |   7 ++
 7 files changed, 171 insertions(+), 41 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index 3b4b595c..3b36b6f0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -108,7 +108,7 @@ LIBOBJ_NW = stdlib/snprintf.$(O) stdlib/vsnprintf.$(O) stdlib/strlcpy.$(O) \
 	\
 	nasmlib/ver.$(O) \
 	nasmlib/alloc.$(O) nasmlib/asprintf.$(O) nasmlib/errfile.$(O) \
-	nasmlib/crc64.$(O) nasmlib/md5c.$(O) \
+	nasmlib/crc32.$(O) nasmlib/crc64.$(O) nasmlib/md5c.$(O) \
 	nasmlib/string.$(O) nasmlib/nctype.$(O) \
 	nasmlib/file.$(O) nasmlib/mmap.$(O) nasmlib/ilog2.$(O) \
 	nasmlib/realpath.$(O) nasmlib/path.$(O) \
diff --git a/Mkfiles/msvc.mak b/Mkfiles/msvc.mak
index 4d51bcf8..0b8308f2 100644
--- a/Mkfiles/msvc.mak
+++ b/Mkfiles/msvc.mak
@@ -72,7 +72,7 @@ LIBOBJ_NW = stdlib\snprintf.$(O) stdlib\vsnprintf.$(O) stdlib\strlcpy.$(O) \
 	\
 	nasmlib\ver.$(O) \
 	nasmlib\alloc.$(O) nasmlib\asprintf.$(O) nasmlib\errfile.$(O) \
-	nasmlib\crc64.$(O) nasmlib\md5c.$(O) \
+	nasmlib\crc32.$(O) nasmlib\crc64.$(O) nasmlib\md5c.$(O) \
 	nasmlib\string.$(O) nasmlib\nctype.$(O) \
 	nasmlib\file.$(O) nasmlib\mmap.$(O) nasmlib\ilog2.$(O) \
 	nasmlib\realpath.$(O) nasmlib\path.$(O) \
diff --git a/Mkfiles/openwcom.mak b/Mkfiles/openwcom.mak
index 3c5ca235..219a48e9 100644
--- a/Mkfiles/openwcom.mak
+++ b/Mkfiles/openwcom.mak
@@ -61,7 +61,7 @@ LIBOBJ_NW = stdlib\snprintf.$(O) stdlib\vsnprintf.$(O) stdlib\strlcpy.$(O) &
 	&
 	nasmlib\ver.$(O) &
 	nasmlib\alloc.$(O) nasmlib\asprintf.$(O) nasmlib\errfile.$(O) &
-	nasmlib\crc64.$(O) nasmlib\md5c.$(O) &
+	nasmlib\crc32.$(O) nasmlib\crc64.$(O) nasmlib\md5c.$(O) &
 	nasmlib\string.$(O) nasmlib\nctype.$(O) &
 	nasmlib\file.$(O) nasmlib\mmap.$(O) nasmlib\ilog2.$(O) &
 	nasmlib\realpath.$(O) nasmlib\path.$(O) &
diff --git a/include/hashtbl.h b/include/hashtbl.h
index e84d5061..9ea94dcb 100644
--- a/include/hashtbl.h
+++ b/include/hashtbl.h
@@ -79,6 +79,8 @@ static inline uint64_t crc64_byte(uint64_t crc, uint8_t v)
     return crc64_tab[(uint8_t)(v ^ crc)] ^ (crc >> 8);
 }
 
+uint32_t crc32b(uint32_t crc, const void *data, size_t len);
+
 void **hash_find(struct hash_table *head, const char *string,
 		struct hash_insert *insert);
 void **hash_findb(struct hash_table *head, const void *key, size_t keylen,
diff --git a/rdoff/hash.c b/nasmlib/crc32.c
similarity index 91%
copy from rdoff/hash.c
copy to nasmlib/crc32.c
index f2afad0f..40b914ef 100644
--- a/rdoff/hash.c
+++ b/nasmlib/crc32.c
@@ -1,6 +1,6 @@
 /* ----------------------------------------------------------------------- *
  *
- *   Copyright 1996-2009 The NASM Authors - All Rights Reserved
+ *   Copyright 1996-2021 The NASM Authors - All Rights Reserved
  *   See the file AUTHORS included with the NASM distribution for
  *   the specific copyright holders.
  *
@@ -31,17 +31,10 @@
  *
  * ----------------------------------------------------------------------- */
 
-/*
- * hash.h     Routines to calculate a CRC32 hash value
- *
- *   These routines donated to the NASM effort by Graeme Defty.
- */
-
 #include "compiler.h"
+#include "hashtbl.h"
 
-#include "hash.h"
-
-const uint32_t consttab[] = {
+const uint32_t crc32_tab[256] = {
     0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
     0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
     0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
@@ -108,15 +101,15 @@ const uint32_t consttab[] = {
     0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
 };
 
-uint32_t hash(const char *name)
+uint32_t crc32b(uint32_t crc, const void *data, size_t len)
 {
-    register const char *n;
-    register uint32_t hashval = 0xffffffff;
+    register const uint8_t *p = data;
+    register uint32_t hashval = crc;
 
-    for (n = name; *n; n++)
-        hashval = (hashval >> 8) ^ consttab[(hashval ^ *n) & 0xff];
-
-    hashval ^= 0xffffffff;
+    while (len--)
+    {
+        hashval = (hashval >> 8) ^ crc32_tab[(hashval ^ *p++) & 0xff];
+    }
 
     return hashval;
-}
+};
diff --git a/output/outcoff.c b/output/outcoff.c
index 58fa0249..a8159ba7 100644
--- a/output/outcoff.c
+++ b/output/outcoff.c
@@ -184,6 +184,7 @@ static void coff_write(void);
 static void coff_section_header(char *, int32_t, int32_t, int32_t, int32_t, int32_t, int, int32_t);
 static void coff_write_relocs(struct coff_Section *);
 static void coff_write_symbols(void);
+static void coff_defcomdatname(char *name, int32_t segment);
 
 static void coff_win32_init(void)
 {
@@ -238,6 +239,7 @@ static void coff_cleanup(void)
             nasm_free(r);
         }
         nasm_free(coff_sects[i]->name);
+        nasm_free(coff_sects[i]->comdat_name);
         nasm_free(coff_sects[i]);
     }
     nasm_free(coff_sects);
@@ -306,9 +308,10 @@ static inline unsigned int coff_alignment(uint32_t flags)
 
 static int32_t coff_section_names(char *name, int *bits)
 {
-    char *p;
+    char *p, *comdat_name;
     uint32_t flags, align_flags;
-    int i;
+    int i, j;
+    int8_t comdat_selection;
 
     /*
      * Set default bits.
@@ -333,7 +336,8 @@ static int32_t coff_section_names(char *name, int *bits)
             name[8] = '\0';
         }
     }
-    flags = align_flags = 0;
+    flags = align_flags = comdat_selection = 0;
+    comdat_name = NULL;
 
     while (*p && nasm_isspace(*p))
         p++;
@@ -386,12 +390,45 @@ static int32_t coff_section_names(char *name, int *bits)
                     align_flags = coff_sectalign_flags(align);
                 }
             }
+        } else if (!nasm_strnicmp(q, "comdat=", 7)) {
+            /*
+             * Expected format: comdat=num:name]
+             * where
+             *   num is a number: one of the IMAGE_COMDAT_SELECT_* constants
+             *   name is a string: the "COMDAT name"
+             */
+            comdat_selection = strtoul(q + 7, &q, 10);
+            if (!comdat_selection)
+                nasm_nonfatal("invalid argument to `comdat'");
+            else if (*q != ':')
+                nasm_nonfatal("missing name in `comdat'");
+            else {
+                comdat_name = q + 1;
+            }
         }
     }
 
     for (i = 0; i < coff_nsects; i++)
-        if (!strcmp(name, coff_sects[i]->name))
-            break;
+        if (!strcmp(name, coff_sects[i]->name)) {
+            if (!comdat_name && !coff_sects[i]->comdat_name)
+                break;
+            else if (comdat_name && coff_sects[i]->comdat_name &&
+                     !strcmp(comdat_name, coff_sects[i]->comdat_name)) {
+                /*
+                 * For COMDAT, it makes sense to have multiple sections with
+                 * the same name (different comdat name though)
+                 */
+                if ((coff_sects[i]->comdat_selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE &&
+                    comdat_selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) ||
+                    (coff_sects[i]->comdat_selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE &&
+                    comdat_selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE)) {
+                    /*
+                     * Let's also allow an associative/other pair with the same name
+                     */
+                    break;
+                }
+            }
+        }
     if (i == coff_nsects) {
         if (!flags) {
             flags = TEXT_FLAGS;
@@ -409,10 +446,37 @@ static int32_t coff_section_names(char *name, int *bits)
                     flags = XDATA_FLAGS;
             }
         }
+
+        if (comdat_name)
+            flags |= IMAGE_SCN_LNK_COMDAT;
+
         i = coff_make_section(name, flags);
         coff_sects[i]->align_flags = align_flags;
+
+        if (comdat_name) {
+            coff_sects[i]->comdat_selection = comdat_selection;
+
+            coff_sects[i]->comdat_name = strdup(comdat_name);
+            if (comdat_selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
+                /*
+                 * Find a previous section with given comdat name
+                 */ 
+                for (j = 0; j < coff_nsects - 1; j++)
+                    if (coff_sects[j]->comdat_name &&
+                        !strcmp(coff_sects[j]->comdat_name, comdat_name))
+                        break;
+                if (j < coff_nsects - 1) {
+                    coff_sects[i]->comdat_associated = j + 1;
+                }
+                else
+                    nasm_nonfatal("unknown `comdat' associative");
+            }
+        }
     } else {
         if (flags) {
+            if (comdat_name)
+                flags |= IMAGE_SCN_LNK_COMDAT;
+
             /* Warn if non-alignment flags differ */
             if (((flags ^ coff_sects[i]->flags) & ~IMAGE_SCN_ALIGN_MASK) &&
                 coff_sects[i]->pass_last_seen == pass_count()) {
@@ -428,6 +492,14 @@ static int32_t coff_section_names(char *name, int *bits)
         if (align_flags > coff_sects[i]->align_flags) {
             coff_sects[i]->align_flags = align_flags;
         }
+
+        if (comdat_name) {
+            if ((coff_sects[i]->comdat_selection != comdat_selection) &&
+                coff_sects[i]->pass_last_seen == pass_count()) {
+                nasm_warn(WARN_OTHER, "comdat selection changed on"
+                          " redeclaration of name `%s'", comdat_name);
+            }
+        }
     }
 
     coff_sects[i]->pass_last_seen = pass_count();
@@ -437,7 +509,7 @@ static int32_t coff_section_names(char *name, int *bits)
 static void coff_deflabel(char *name, int32_t segment, int64_t offset,
                           int is_global, char *special)
 {
-    int pos = strslen + 4;
+    int pos, section;
     struct coff_Symbol *sym;
 
     if (special)
@@ -450,6 +522,32 @@ static void coff_deflabel(char *name, int32_t segment, int64_t offset,
         return;
     }
 
+    if (segment == NO_SEG)
+        section = -1;      /* absolute symbol */
+    else {
+        int i;
+        section = 0;
+        for (i = 0; i < coff_nsects; i++)
+            if (segment == coff_sects[i]->index) {
+                section = i + 1;
+
+                if (coff_sects[i]->comdat_name && !coff_sects[i]->comdat_symbol) {
+                    /*
+                     * The "comdat symbol" must be the first one in symbol table
+                     * So we'll insert/define it - before defining the other one
+                     */
+                    coff_sects[i]->comdat_symbol = 1;
+
+                    if (coff_sects[i]->comdat_selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE &&
+                        0 != strcmp(coff_sects[i]->comdat_name, name)) {
+                        coff_defcomdatname(coff_sects[i]->comdat_name, segment);
+                    }
+                }
+                break;
+            }
+    }
+
+    pos = strslen + 4;
     if (strlen(name) > 8) {
         size_t nlen = strlen(name)+1;
         saa_wbytes(coff_strs, name, nlen);
@@ -465,19 +563,9 @@ static void coff_deflabel(char *name, int32_t segment, int64_t offset,
         strcpy(sym->name, name);
     sym->is_global = !!is_global;
     sym->type = 0;              /* Default to T_NULL (no type) */
-    if (segment == NO_SEG)
-        sym->section = -1;      /* absolute symbol */
-    else {
-        int i;
-        sym->section = 0;
-        for (i = 0; i < coff_nsects; i++)
-            if (segment == coff_sects[i]->index) {
-                sym->section = i + 1;
-                break;
-            }
-        if (!sym->section)
-            sym->is_global = true;
-    }
+    sym->section = section;
+    if (!sym->section)
+        sym->is_global = true;
     if (is_global == 2)
         sym->value = offset;
     else
@@ -740,6 +828,11 @@ static void BuildExportTable(STRING **rvp)
     *rvp = NULL;
 }
 
+static void coff_defcomdatname(char *name, int32_t segment)
+{
+    coff_deflabel(name, segment, 0, 1, NULL);
+}
+
 static enum directive_result
 coff_directives(enum directive directive, char *value)
 {
@@ -906,6 +999,17 @@ static void coff_write(void)
             coff_deflabel("@feat.00", NO_SEG, 1, 0, NULL);
     }
 
+    /*
+     * Check if all comdat sections have their comdat symbol
+     * If not, define it
+     */
+    for (i = 0; i < coff_nsects; i++) {
+        if (coff_sects[i]->comdat_name && !coff_sects[i]->comdat_symbol &&
+            coff_sects[i]->comdat_selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
+            coff_defcomdatname(coff_sects[i]->comdat_name, coff_sects[i]->index);
+        }
+    }
+
     /*
      * Work out how big the file will get.
      * Calculate the start of the `real' symbols at the same time.
@@ -961,6 +1065,22 @@ static void coff_write(void)
         if (coff_sects[i]->data) {
             saa_fpwrite(coff_sects[i]->data, ofile);
             coff_write_relocs(coff_sects[i]);
+
+            if (coff_sects[i]->flags & IMAGE_SCN_LNK_COMDAT) {
+                /*
+                 * Checksum the section data
+                 */
+                uint32_t checksum = 0;
+                const char *data;
+                size_t len;
+
+                saa_rewind(coff_sects[i]->data);
+                while (len = coff_sects[i]->data->datalen,
+                        (data = saa_rbytes(coff_sects[i]->data, &len)) != NULL)
+                    checksum = crc32b(checksum, data, len);
+
+                coff_sects[i]->checksum = checksum;
+            }
         }
 
     /*
@@ -1091,7 +1211,15 @@ static void coff_write_symbols(void)
         coff_symbol(coff_sects[i]->name, 0L, 0L, i + 1, 0, 3, 1);
         fwriteint32_t(coff_sects[i]->len,    ofile);
         fwriteint16_t(coff_sects[i]->nrelocs,ofile);
-        nasm_write(filename, 12, ofile);
+        if (coff_sects[i]->flags & IMAGE_SCN_LNK_COMDAT) {
+            fwriteint16_t(0, ofile);
+            fwriteint32_t(coff_sects[i]->checksum, ofile);
+            fwriteint16_t(coff_sects[i]->comdat_associated, ofile);
+            fputc(coff_sects[i]->comdat_selection, ofile);
+            nasm_write(filename, 3, ofile);
+        }
+        else
+            nasm_write(filename, 12, ofile);
     }
 
     /*
diff --git a/output/pecoff.h b/output/pecoff.h
index efecd0f9..b99bed09 100644
--- a/output/pecoff.h
+++ b/output/pecoff.h
@@ -487,6 +487,13 @@ struct coff_Section {
     int32_t namepos;            /* Offset of name into the strings table */
     int32_t pos, relpos;
     int64_t pass_last_seen;
+
+    /* comdat-related members */
+    char *comdat_name;
+    uint32_t checksum;          /* set only for comdat sections */
+    int8_t comdat_selection;
+    int8_t comdat_symbol;       /* is the "comdat name" in symbol table? */
+    int32_t comdat_associated;  /* associated section for selection==5 */
 };
 
 struct coff_Reloc {


More information about the Nasm-commits mailing list