outobj: emit file dependency information

Some OMF toolchain can make use of file dependency information
embedded in the object files.  As implemented here, we don't try to
absolutize the filenames, as that prevents moving around trees and is
OS-dependent.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
H. Peter Anvin 2017-08-16 14:53:18 -07:00
parent f283c8f5c2
commit a771be85f4
7 changed files with 397 additions and 54 deletions

View file

@ -89,3 +89,4 @@ uppercase ; outieee, outobj
subsections_via_symbols ; macho
no_dead_strip ; macho
maxdump ; dbg
nodepend ; obj

View file

@ -135,6 +135,7 @@ static bool depend_emit_phony = false;
static bool depend_missing_ok = false;
static const char *depend_target = NULL;
static const char *depend_file = NULL;
StrList *depend_list;
static bool want_usage;
static bool terminate_after_phase;
@ -307,7 +308,7 @@ static void timestamp(void)
int main(int argc, char **argv)
{
StrList *depend_list = NULL, **depend_ptr;
StrList **depend_ptr;
timestamp();

View file

@ -385,6 +385,9 @@ struct preproc_ops {
extern const struct preproc_ops nasmpp;
extern const struct preproc_ops preproc_nop;
/* List of dependency files */
extern StrList *depend_list;
/*
* Some lexical properties of the NASM source language, included
* here because they are shared between the parser and preprocessor.

View file

@ -448,6 +448,7 @@ const void *nasm_map_file(FILE *fp, off_t start, off_t len);
void nasm_unmap_file(const void *p, size_t len);
off_t nasm_file_size(FILE *f);
off_t nasm_file_size_by_path(const char *pathname);
bool nasm_file_time(time_t *t, const char *pathname);
void fwritezero(off_t bytes, FILE *fp);
static inline bool const_func overflow_general(int64_t value, int bytes)

View file

@ -13,6 +13,8 @@
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
@ -64,22 +66,69 @@ static const char *record_types[256] =
typedef void (*dump_func)(uint8_t, const uint8_t *, size_t);
static void hexdump_data(unsigned int offset, const uint8_t *data, size_t n)
/* Ordered collection type */
struct collection {
size_t n; /* Elements in collection (not including 0) */
size_t s; /* Elements allocated (not including 0) */
const void **p; /* Element pointers */
};
struct collection c_names, c_lsegs, c_groups, c_extsym;
static void nomem(void)
{
fprintf(stderr, "%s: memory allocation error\n", progname);
exit(1);
}
#define INIT_SIZE 64
static void add_collection(struct collection *c, const void *p)
{
if (c->n >= c->s) {
size_t cs = c->s ? (c->s << 1) : INIT_SIZE;
const void **cp = realloc(c->p, cs*sizeof(const void *));
if (!cp)
nomem();
c->p = cp;
c->s = cs;
memset(cp + c->n, 0, (cs - c->n)*sizeof(const void *));
}
c->p[++c->n] = p;
}
static const void *get_collection(struct collection *c, size_t index)
{
if (index >= c->n)
return NULL;
return c->p[index];
}
static void hexdump_data(unsigned int offset, const uint8_t *data,
size_t n, size_t field)
{
unsigned int i, j;
for (i = 0; i < n; i += 16) {
printf(" %04x: ", i+offset);
printf(" %04x: ", i+offset);
for (j = 0; j < 16; j++) {
if (i+j < n)
printf("%02x%c", data[i+j], (j == 7) ? '-' : ' ');
else
printf(" ");
char sep = (j == 7) ? '-' : ' ';
if (i+j < field)
printf("%02x%c", data[i+j], sep);
else if (i+j < n)
printf("xx%c", sep); /* Beyond end of... */
else
printf(" "); /* No separator */
}
printf(" : ");
for (j = 0; j < 16; j++) {
if (i+j < n)
putchar(isprint(data[i+j]) ? data[i+j] : '.');
if (i+j < n)
putchar((i+j >= field) ? 'x' :
isprint(data[i+j]) ? data[i+j] : '.');
}
putchar('\n');
}
@ -88,7 +137,7 @@ static void hexdump_data(unsigned int offset, const uint8_t *data, size_t n)
static void dump_unknown(uint8_t type, const uint8_t *data, size_t n)
{
(void)type;
hexdump_data(0, data, n);
hexdump_data(0, data, n, n);
}
static void dump_coment(uint8_t type, const uint8_t *data, size_t n)
@ -116,31 +165,248 @@ static void dump_coment(uint8_t type, const uint8_t *data, size_t n)
[0xdc] = "Date",
[0xdd] = "Timestamp",
[0xdf] = "User",
[0xe3] = "Type definition",
[0xe8] = "Filename",
[0xe9] = "Dependency file",
[0xff] = "Command line"
};
if (n < 2) {
dump_unknown(type, data, n);
hexdump_data(type, data, 2, n);
return;
}
type = data[0];
class = data[1];
printf(" [NP=%d NL=%d UD=%02X] %02X %s\n",
printf(" [NP=%d NL=%d UD=%02X] %02X %s\n",
(type >> 7) & 1,
(type >> 6) & 1,
type & 0x3f,
class,
coment_class[class] ? coment_class[class] : "???");
hexdump_data(2, data+2, n-2);
hexdump_data(2, data+2, n-2, n-2);
}
/* Parse an index field */
static uint16_t get_index(const uint8_t **pp)
{
uint8_t c;
c = *(*pp)++;
if (c & 0x80) {
return ((c & 0x7f) << 8) + *(*pp)++;
} else {
return c;
}
}
static uint16_t get_16(const uint8_t **pp)
{
uint16_t v = *(const uint16_t *)(*pp);
(*pp) += 2;
return v;
}
static uint32_t get_32(const uint8_t **pp)
{
const uint32_t v = *(const uint32_t *)(*pp);
(*pp) += 4;
return v;
}
/* Returns a name as a C string in a newly allocated buffer */
char *lname(int index)
{
char *s;
const char *p = get_collection(&c_names, index);
size_t len;
if (!p)
return NULL;
len = (uint8_t)p[0];
s = malloc(len+1);
if (!s)
nomem();
memcpy(s, p+1, len);
s[len] = '\0';
return s;
}
/* LNAMES or LLNAMES */
static void dump_lnames(uint8_t type, const uint8_t *data, size_t n)
{
const uint8_t *p = data;
const uint8_t *end = data + n;
while (p < end) {
size_t l = *p+1;
if (l > n) {
add_collection(&c_names, NULL);
printf(" # %4u 0x%04x: \"%.*s... <%zu missing bytes>\n",
c_names.n, c_names.n, n-1, p+1, l-n);
} else {
add_collection(&c_names, p);
printf(" # %4u 0x%04x: \"%.*s\"\n",
c_names.n, c_names.n, l-1, p+1);
}
hexdump_data(p-data, p, l, n);
p += l;
n -= l;
}
}
/* SEGDEF16 or SEGDEF32 */
static void dump_segdef(uint8_t type, const uint8_t *data, size_t n)
{
bool big = type & 1;
const uint8_t *p = data;
const uint8_t *end = data+n;
uint8_t attr;
static const char * const alignment[8] =
{ "ABS", "BYTE", "WORD", "PARA", "PAGE", "DWORD", "LTL", "?ALIGN" };
static const char * const combine[8] =
{ "PRIVATE", "?COMMON", "PUBLIC", "?COMBINE", "?PUBLIC", "STACK", "COMMON", "?PUBLIC" };
uint16_t idx;
char *s;
if (p >= end)
return;
attr = *p++;
printf(" # %s (A%u) %s (C%u) %s%s",
alignment[(attr >> 5) & 7], (attr >> 5) & 7,
combine[(attr >> 2) & 7], (attr >> 2) & 7,
(attr & 0x02) ? "MAXSIZE " : "",
(attr & 0x01) ? "USE32" : "USE16");
if (((attr >> 5) & 7) == 0) {
/* Absolute segment */
if (p+3 > end)
goto dump;
printf(" AT %04x:", get_16(&p));
printf("%02x", *p++);
}
if (big) {
if (p+4 > end)
goto dump;
printf(" size 0x%08x", get_32(&p));
} else {
if (p+2 > end)
goto dump;
printf(" size 0x%04x", get_16(&p));
}
idx = get_index(&p);
if (p > end)
goto dump;
s = lname(idx);
printf(" name '%s'", s);
idx = get_index(&p);
if (p > end)
goto dump;
s = lname(idx);
printf(" class '%s'", s);
idx = get_index(&p);
if (p > end)
goto dump;
s = lname(idx);
printf(" ovl '%s'", s);
dump:
putchar('\n');
hexdump_data(0, data, n, n);
}
/* FIXUPP16 or FIXUPP32 */
static void dump_fixupp(uint8_t type, const uint8_t *data, size_t n)
{
bool big = type & 1;
const uint8_t *p = data;
const uint8_t *end = data + n;
static const char * const method_base[4] =
{ "SEGDEF", "GRPDEF", "EXTDEF", "frame#" };
while (p < end) {
const uint8_t *start = p;
uint8_t op = *p++;
uint16_t index;
uint32_t disp;
if (!(op & 0x80)) {
/* THREAD record */
bool frame = !!(op & 0x40);
printf(" THREAD %-7s%d%s method %c%d (%s)",
frame ? "frame" : "target", op & 3,
(op & 0x20) ? " +flag5?" : "",
(op & 0x40) ? 'F' : 'T',
op & 3, method_base[op & 3]);
if ((op & 0x50) != 0x50) {
printf(" index 0x%04x", get_index(&p));
}
putchar('\n');
} else {
/* FIXUP subrecord */
uint8_t fix;
printf(" FIXUP %s-rel location %2d offset 0x%03x",
(op & 0x40) ? "seg" : "self",
(op & 0x3c) >> 2,
((op & 3) << 8) + *p++);
fix = *p++;
printf("\n frame %s%d%s",
(fix & 0x80) ? "thread " : "F",
((fix & 0x70) >> 4),
((fix & 0xc0) == 0xc0) ? "?" : "");
if ((fix & 0xc0) == 0)
printf(" datum 0x%04x", get_index(&p));
printf("\n target %s%d",
(fix & 0x10) ? "thread " : "method T",
fix & 3);
if ((fix & 0x10) == 0)
printf(" (%s)", method_base[fix & 3]);
printf(" datum 0x%04x", get_index(&p));
if ((fix & 0x08) == 0) {
if (big) {
printf(" disp 0x%08x", get_32(&p));
} else {
printf(" disp 0x%04x", get_16(&p));
}
}
putchar('\n');
}
hexdump_data(start-data, start, p-start, n-(start-data));
}
}
static const dump_func dump_type[256] =
{
[0x88] = dump_coment,
[0x96] = dump_lnames,
[0x98] = dump_segdef,
[0x99] = dump_segdef,
[0x9c] = dump_fixupp,
[0x9d] = dump_fixupp,
[0xca] = dump_lnames,
};
int dump_omf(int fd)

View file

@ -239,3 +239,21 @@ off_t nasm_file_size_by_path(const char *pathname)
return len;
#endif
}
/*
* Report the timestamp on a file, returns true if successful
*/
bool nasm_file_time(time_t *t, const char *pathname)
{
#ifdef nasm_stat
nasm_struct_stat st;
if (nasm_stat(pathname, &st))
return false;
*t = st.st_mtime;
return true;
#else
return false; /* No idea how to do this on this OS */
#endif
}

View file

@ -1,6 +1,6 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 1996-2016 The NASM Authors - All Rights Reserved
*
* Copyright 1996-2017 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
@ -148,13 +148,15 @@ enum RecordID { /* record ID codes */
};
enum ComentID { /* ID codes for comment records */
dEXTENDED = 0xA1, /* tells that we are using translator-specific extensions */
dLINKPASS = 0xA2, /* link pass 2 marker */
dTYPEDEF = 0xE3, /* define a type */
dSYM = 0xE6, /* symbol debug record */
dFILNAME = 0xE8, /* file name record */
dCOMPDEF = 0xEA /* compiler type info */
dTRANSL = 0x0000, /* translator comment */
dOMFEXT = 0xC0A0, /* "OMF extension" */
dEXTENDED = 0xC0A1, /* translator-specific extensions */
dLINKPASS = 0x40A2, /* link pass 2 marker */
dTYPEDEF = 0xC0E3, /* define a type */
dSYM = 0xC0E6, /* symbol debug record */
dFILNAME = 0xC0E8, /* file name record */
dDEPFILE = 0xC0E9, /* dependency file */
dCOMPDEF = 0xC0EA /* compiler type info */
};
typedef struct ObjRecord ObjRecord;
@ -181,6 +183,7 @@ static ObjRecord *obj_commit(ObjRecord * orp);
static bool obj_uppercase; /* Flag: all names in uppercase */
static bool obj_use32; /* Flag: at least one segment is 32-bit */
static bool obj_nodepend; /* Flag: don't emit file dependencies */
/*
* Clear an ObjRecord structure. (Never reallocates).
@ -483,8 +486,7 @@ static void ori_linnum(ObjRecord * orp)
*/
static void ori_local(ObjRecord * orp)
{
obj_byte(orp, 0x40);
obj_byte(orp, dSYM);
obj_rword(orp, dSYM);
}
/*
@ -1944,6 +1946,31 @@ static void obj_filename(char *inname, char *outname)
standard_extension(inname, outname, ".obj");
}
/* Get a file timestamp in MS-DOS format */
static uint32_t obj_file_timestamp(const char *pathname)
{
time_t t;
const struct tm *lt;
if (!nasm_file_time(&t, pathname))
return 0;
lt = localtime(&t);
if (!lt)
return 0;
if (lt->tm_year < 80 || lt->tm_year > 207)
return 0; /* Only years 1980-2107 representable */
return
((uint32_t)lt->tm_sec >> 1) +
((uint32_t)lt->tm_min << 5) +
((uint32_t)lt->tm_hour << 11) +
((uint32_t)lt->tm_mday << 16) +
(((uint32_t)lt->tm_mon + 1) << 21) +
(((uint32_t)lt->tm_year - 80) << 25);
}
static void obj_write_file(void)
{
struct Segment *seg, *entry_seg_ptr = 0;
@ -1956,6 +1983,7 @@ static void obj_write_file(void)
struct ExpDef *export;
int lname_idx;
ObjRecord *orp;
const StrList *depfile;
const bool debuginfo = (dfmt == &borland_debug_form);
/*
@ -1970,16 +1998,34 @@ static void obj_write_file(void)
* Write the NASM boast comment.
*/
orp->type = COMENT;
obj_rword(orp, 0); /* comment type zero */
obj_rword(orp, dTRANSL);
obj_name(orp, nasm_comment);
obj_emit2(orp);
/*
* Output file dependency information
*/
if (!obj_nodepend) {
list_for_each(depfile, depend_list) {
uint32_t ts;
ts = obj_file_timestamp(depfile->str);
if (ts) {
orp->type = COMENT;
obj_rword(orp, dDEPFILE);
obj_dword(orp, ts);
obj_name(orp, depfile->str);
obj_emit2(orp);
}
}
}
orp->type = COMENT;
/*
* Write the IMPDEF records, if any.
*/
for (imp = imphead; imp; imp = imp->next) {
obj_rword(orp, 0xA0); /* comment class A0 */
obj_rword(orp, dOMFEXT);
obj_byte(orp, 1); /* subfunction 1: IMPDEF */
if (imp->impname)
obj_byte(orp, 0); /* import by name */
@ -1998,7 +2044,7 @@ static void obj_write_file(void)
* Write the EXPDEF records, if any.
*/
for (export = exphead; export; export = export->next) {
obj_rword(orp, 0xA0); /* comment class A0 */
obj_rword(orp, dOMFEXT);
obj_byte(orp, 2); /* subfunction 2: EXPDEF */
obj_byte(orp, export->flags);
obj_name(orp, export->extname);
@ -2011,8 +2057,7 @@ static void obj_write_file(void)
/* we're using extended OMF if we put in debug info */
if (debuginfo) {
orp->type = COMENT;
obj_byte(orp, 0x40);
obj_byte(orp, dEXTENDED);
obj_rword(orp, dEXTENDED);
obj_emit2(orp);
}
@ -2188,8 +2233,7 @@ static void obj_write_file(void)
*/
if (debuginfo || obj_entry_seg == NO_SEG) {
orp->type = COMENT;
obj_byte(orp, 0x40);
obj_byte(orp, dLINKPASS);
obj_rword(orp, dLINKPASS);
obj_byte(orp, 1);
obj_emit2(orp);
}
@ -2202,34 +2246,29 @@ static void obj_write_file(void)
int i;
struct Array *arrtmp = arrhead;
orp->type = COMENT;
obj_byte(orp, 0x40);
obj_byte(orp, dCOMPDEF);
obj_rword(orp, dCOMPDEF);
obj_byte(orp, 4);
obj_byte(orp, 0);
obj_emit2(orp);
obj_byte(orp, 0x40);
obj_byte(orp, dTYPEDEF);
obj_rword(orp, dTYPEDEF);
obj_word(orp, 0x18); /* type # for linking */
obj_word(orp, 6); /* size of type */
obj_byte(orp, 0x2a); /* absolute type for debugging */
obj_emit2(orp);
obj_byte(orp, 0x40);
obj_byte(orp, dTYPEDEF);
obj_rword(orp, dTYPEDEF);
obj_word(orp, 0x19); /* type # for linking */
obj_word(orp, 0); /* size of type */
obj_byte(orp, 0x24); /* absolute type for debugging */
obj_byte(orp, 0); /* near/far specifier */
obj_emit2(orp);
obj_byte(orp, 0x40);
obj_byte(orp, dTYPEDEF);
obj_rword(orp, dTYPEDEF);
obj_word(orp, 0x1A); /* type # for linking */
obj_word(orp, 0); /* size of type */
obj_byte(orp, 0x24); /* absolute type for debugging */
obj_byte(orp, 1); /* near/far specifier */
obj_emit2(orp);
obj_byte(orp, 0x40);
obj_byte(orp, dTYPEDEF);
obj_rword(orp, dTYPEDEF);
obj_word(orp, 0x1b); /* type # for linking */
obj_word(orp, 0); /* size of type */
obj_byte(orp, 0x23); /* absolute type for debugging */
@ -2237,8 +2276,7 @@ static void obj_write_file(void)
obj_byte(orp, 0);
obj_byte(orp, 0);
obj_emit2(orp);
obj_byte(orp, 0x40);
obj_byte(orp, dTYPEDEF);
obj_rword(orp, dTYPEDEF);
obj_word(orp, 0x1c); /* type # for linking */
obj_word(orp, 0); /* size of type */
obj_byte(orp, 0x23); /* absolute type for debugging */
@ -2246,8 +2284,7 @@ static void obj_write_file(void)
obj_byte(orp, 4);
obj_byte(orp, 0);
obj_emit2(orp);
obj_byte(orp, 0x40);
obj_byte(orp, dTYPEDEF);
obj_rword(orp, dTYPEDEF);
obj_word(orp, 0x1d); /* type # for linking */
obj_word(orp, 0); /* size of type */
obj_byte(orp, 0x23); /* absolute type for debugging */
@ -2255,8 +2292,7 @@ static void obj_write_file(void)
obj_byte(orp, 1);
obj_byte(orp, 0);
obj_emit2(orp);
obj_byte(orp, 0x40);
obj_byte(orp, dTYPEDEF);
obj_rword(orp, dTYPEDEF);
obj_word(orp, 0x1e); /* type # for linking */
obj_word(orp, 0); /* size of type */
obj_byte(orp, 0x23); /* absolute type for debugging */
@ -2267,8 +2303,7 @@ static void obj_write_file(void)
/* put out the array types */
for (i = ARRAYBOT; i < arrindex; i++) {
obj_byte(orp, 0x40);
obj_byte(orp, dTYPEDEF);
obj_rword(orp, dTYPEDEF);
obj_word(orp, i); /* type # for linking */
obj_word(orp, arrtmp->size); /* size of type */
obj_byte(orp, 0x1A); /* absolute type for debugging (array) */
@ -2291,8 +2326,7 @@ static void obj_write_file(void)
/* write out current file name */
orp->type = COMENT;
orp->ori = ori_null;
obj_byte(orp, 0x40);
obj_byte(orp, dFILNAME);
obj_rword(orp, dFILNAME);
obj_byte(orp, 0);
obj_name(orp, fn->name);
obj_dword(orp, 0);
@ -2431,6 +2465,21 @@ static void obj_fwrite(ObjRecord * orp)
fputc((-cksum) & 0xFF, ofile);
}
static enum directive_result
obj_pragma(const struct pragma *pragma)
{
switch (pragma->opcode) {
case D_NODEPEND:
obj_nodepend = true;
break;
default:
break;
}
return DIRR_OK;
}
extern macros_t obj_stdmac[];
static void dbgbi_init(void)
@ -2608,7 +2657,7 @@ static void dbgbi_typevalue(int32_t type)
vsize = 10;
break;
default:
last_defined->type = 0x19; /*label */
last_defined->type = 0x19; /* label */
vsize = 0;
break;
}
@ -2649,6 +2698,10 @@ static const struct dfmt * const borland_debug_arr[3] = {
NULL
};
static const struct pragma_facility obj_pragma_list[] = {
{ NULL, obj_pragma }
};
const struct ofmt of_obj = {
"MS-DOS 16-bit/32-bit OMF object files",
"obj",
@ -2667,6 +2720,6 @@ const struct ofmt of_obj = {
obj_directive,
obj_filename,
obj_cleanup,
NULL /* pragma list */
obj_pragma_list
};
#endif /* OF_OBJ */