1428 lines
32 KiB
Plaintext
1428 lines
32 KiB
Plaintext
%{ /* deffilep.y - parser for .def files */
|
|
|
|
/* Copyright 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006,
|
|
2007, 2009 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Binutils.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#include "sysdep.h"
|
|
#include "libiberty.h"
|
|
#include "safe-ctype.h"
|
|
#include "bfd.h"
|
|
#include "ld.h"
|
|
#include "ldmisc.h"
|
|
#include "deffile.h"
|
|
|
|
#define TRACE 0
|
|
|
|
#define ROUND_UP(a, b) (((a)+((b)-1))&~((b)-1))
|
|
|
|
/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
|
|
as well as gratuitiously global symbol names, so we can have multiple
|
|
yacc generated parsers in ld. Note that these are only the variables
|
|
produced by yacc. If other parser generators (bison, byacc, etc) produce
|
|
additional global names that conflict at link time, then those parser
|
|
generators need to be fixed instead of adding those names to this list. */
|
|
|
|
#define yymaxdepth def_maxdepth
|
|
#define yyparse def_parse
|
|
#define yylex def_lex
|
|
#define yyerror def_error
|
|
#define yylval def_lval
|
|
#define yychar def_char
|
|
#define yydebug def_debug
|
|
#define yypact def_pact
|
|
#define yyr1 def_r1
|
|
#define yyr2 def_r2
|
|
#define yydef def_def
|
|
#define yychk def_chk
|
|
#define yypgo def_pgo
|
|
#define yyact def_act
|
|
#define yyexca def_exca
|
|
#define yyerrflag def_errflag
|
|
#define yynerrs def_nerrs
|
|
#define yyps def_ps
|
|
#define yypv def_pv
|
|
#define yys def_s
|
|
#define yy_yys def_yys
|
|
#define yystate def_state
|
|
#define yytmp def_tmp
|
|
#define yyv def_v
|
|
#define yy_yyv def_yyv
|
|
#define yyval def_val
|
|
#define yylloc def_lloc
|
|
#define yyreds def_reds /* With YYDEBUG defined. */
|
|
#define yytoks def_toks /* With YYDEBUG defined. */
|
|
#define yylhs def_yylhs
|
|
#define yylen def_yylen
|
|
#define yydefred def_yydefred
|
|
#define yydgoto def_yydgoto
|
|
#define yysindex def_yysindex
|
|
#define yyrindex def_yyrindex
|
|
#define yygindex def_yygindex
|
|
#define yytable def_yytable
|
|
#define yycheck def_yycheck
|
|
|
|
typedef struct def_pool_str {
|
|
struct def_pool_str *next;
|
|
char data[1];
|
|
} def_pool_str;
|
|
|
|
static def_pool_str *pool_strs = NULL;
|
|
|
|
static char *def_pool_alloc (size_t sz);
|
|
static char *def_pool_strdup (const char *str);
|
|
static void def_pool_free (void);
|
|
|
|
static void def_description (const char *);
|
|
static void def_exports (const char *, const char *, int, int, const char *);
|
|
static void def_heapsize (int, int);
|
|
static void def_import (const char *, const char *, const char *, const char *,
|
|
int, const char *);
|
|
static void def_image_name (const char *, int, int);
|
|
static void def_section (const char *, int);
|
|
static void def_section_alt (const char *, const char *);
|
|
static void def_stacksize (int, int);
|
|
static void def_version (int, int);
|
|
static void def_directive (char *);
|
|
static void def_aligncomm (char *str, int align);
|
|
static int def_parse (void);
|
|
static int def_error (const char *);
|
|
static int def_lex (void);
|
|
|
|
static int lex_forced_token = 0;
|
|
static const char *lex_parse_string = 0;
|
|
static const char *lex_parse_string_end = 0;
|
|
|
|
%}
|
|
|
|
%union {
|
|
char *id;
|
|
int number;
|
|
char *digits;
|
|
};
|
|
|
|
%token NAME LIBRARY DESCRIPTION STACKSIZE_K HEAPSIZE CODE DATAU DATAL
|
|
%token SECTIONS EXPORTS IMPORTS VERSIONK BASE CONSTANTU CONSTANTL
|
|
%token PRIVATEU PRIVATEL ALIGNCOMM
|
|
%token READ WRITE EXECUTE SHARED NONAMEU NONAMEL DIRECTIVE EQUAL
|
|
%token <id> ID
|
|
%token <digits> DIGITS
|
|
%type <number> NUMBER
|
|
%type <digits> opt_digits
|
|
%type <number> opt_base opt_ordinal
|
|
%type <number> attr attr_list opt_number exp_opt_list exp_opt
|
|
%type <id> opt_name opt_equal_name dot_name anylang_id opt_id
|
|
%type <id> opt_equalequal_name
|
|
|
|
%%
|
|
|
|
start: start command
|
|
| command
|
|
;
|
|
|
|
command:
|
|
NAME opt_name opt_base { def_image_name ($2, $3, 0); }
|
|
| LIBRARY opt_name opt_base { def_image_name ($2, $3, 1); }
|
|
| DESCRIPTION ID { def_description ($2);}
|
|
| STACKSIZE_K NUMBER opt_number { def_stacksize ($2, $3);}
|
|
| HEAPSIZE NUMBER opt_number { def_heapsize ($2, $3);}
|
|
| CODE attr_list { def_section ("CODE", $2);}
|
|
| DATAU attr_list { def_section ("DATA", $2);}
|
|
| SECTIONS seclist
|
|
| EXPORTS explist
|
|
| IMPORTS implist
|
|
| VERSIONK NUMBER { def_version ($2, 0);}
|
|
| VERSIONK NUMBER '.' NUMBER { def_version ($2, $4);}
|
|
| DIRECTIVE ID { def_directive ($2);}
|
|
| ALIGNCOMM anylang_id ',' NUMBER { def_aligncomm ($2, $4);}
|
|
;
|
|
|
|
|
|
explist:
|
|
/* EMPTY */
|
|
| expline
|
|
| explist expline
|
|
;
|
|
|
|
expline:
|
|
/* The opt_comma is necessary to support both the usual
|
|
DEF file syntax as well as .drectve syntax which
|
|
mandates <expsym>,<expoptlist>. */
|
|
dot_name opt_equal_name opt_ordinal opt_comma exp_opt_list opt_comma opt_equalequal_name
|
|
{ def_exports ($1, $2, $3, $5, $7); }
|
|
;
|
|
exp_opt_list:
|
|
/* The opt_comma is necessary to support both the usual
|
|
DEF file syntax as well as .drectve syntax which
|
|
allows for comma separated opt list. */
|
|
exp_opt opt_comma exp_opt_list { $$ = $1 | $3; }
|
|
| { $$ = 0; }
|
|
;
|
|
exp_opt:
|
|
NONAMEU { $$ = 1; }
|
|
| NONAMEL { $$ = 1; }
|
|
| CONSTANTU { $$ = 2; }
|
|
| CONSTANTL { $$ = 2; }
|
|
| DATAU { $$ = 4; }
|
|
| DATAL { $$ = 4; }
|
|
| PRIVATEU { $$ = 8; }
|
|
| PRIVATEL { $$ = 8; }
|
|
;
|
|
implist:
|
|
implist impline
|
|
| impline
|
|
;
|
|
|
|
impline:
|
|
ID '=' ID '.' ID '.' ID opt_equalequal_name
|
|
{ def_import ($1, $3, $5, $7, -1, $8); }
|
|
| ID '=' ID '.' ID '.' NUMBER opt_equalequal_name
|
|
{ def_import ($1, $3, $5, 0, $7, $8); }
|
|
| ID '=' ID '.' ID opt_equalequal_name
|
|
{ def_import ($1, $3, 0, $5, -1, $6); }
|
|
| ID '=' ID '.' NUMBER opt_equalequal_name
|
|
{ def_import ($1, $3, 0, 0, $5, $6); }
|
|
| ID '.' ID '.' ID opt_equalequal_name
|
|
{ def_import( 0, $1, $3, $5, -1, $6); }
|
|
| ID '.' ID opt_equalequal_name
|
|
{ def_import ( 0, $1, 0, $3, -1, $4); }
|
|
;
|
|
|
|
seclist:
|
|
seclist secline
|
|
| secline
|
|
;
|
|
|
|
secline:
|
|
ID attr_list { def_section ($1, $2);}
|
|
| ID ID { def_section_alt ($1, $2);}
|
|
;
|
|
|
|
attr_list:
|
|
attr_list opt_comma attr { $$ = $1 | $3; }
|
|
| attr { $$ = $1; }
|
|
;
|
|
|
|
opt_comma:
|
|
','
|
|
|
|
|
;
|
|
opt_number: ',' NUMBER { $$=$2;}
|
|
| { $$=-1;}
|
|
;
|
|
|
|
attr:
|
|
READ { $$ = 1;}
|
|
| WRITE { $$ = 2;}
|
|
| EXECUTE { $$=4;}
|
|
| SHARED { $$=8;}
|
|
;
|
|
|
|
opt_name: ID { $$ = $1; }
|
|
| '.' ID
|
|
{
|
|
char *name = def_pool_alloc (strlen ($2) + 2);
|
|
sprintf (name, ".%s", $2);
|
|
$$ = name;
|
|
}
|
|
| ID '.' ID
|
|
{
|
|
char *name = def_pool_alloc (strlen ($1) + 1 + strlen ($3) + 1);
|
|
sprintf (name, "%s.%s", $1, $3);
|
|
$$ = name;
|
|
}
|
|
| { $$ = ""; }
|
|
;
|
|
|
|
opt_equalequal_name: EQUAL ID { $$ = $2; }
|
|
| { $$ = 0; }
|
|
;
|
|
|
|
opt_ordinal:
|
|
'@' NUMBER { $$ = $2;}
|
|
| { $$ = -1;}
|
|
;
|
|
|
|
opt_equal_name:
|
|
'=' dot_name { $$ = $2; }
|
|
| { $$ = 0; }
|
|
;
|
|
|
|
opt_base: BASE '=' NUMBER { $$ = $3;}
|
|
| { $$ = -1;}
|
|
;
|
|
|
|
dot_name: ID { $$ = $1; }
|
|
| '.' ID
|
|
{
|
|
char *name = def_pool_alloc (strlen ($2) + 2);
|
|
sprintf (name, ".%s", $2);
|
|
$$ = name;
|
|
}
|
|
| dot_name '.' ID
|
|
{
|
|
char *name = def_pool_alloc (strlen ($1) + 1 + strlen ($3) + 1);
|
|
sprintf (name, "%s.%s", $1, $3);
|
|
$$ = name;
|
|
}
|
|
;
|
|
|
|
anylang_id: ID { $$ = $1; }
|
|
| '.' ID
|
|
{
|
|
char *id = def_pool_alloc (strlen ($2) + 2);
|
|
sprintf (id, ".%s", $2);
|
|
$$ = id;
|
|
}
|
|
| anylang_id '.' opt_digits opt_id
|
|
{
|
|
char *id = def_pool_alloc (strlen ($1) + 1 + strlen ($3) + strlen ($4) + 1);
|
|
sprintf (id, "%s.%s%s", $1, $3, $4);
|
|
$$ = id;
|
|
}
|
|
;
|
|
|
|
opt_digits: DIGITS { $$ = $1; }
|
|
| { $$ = ""; }
|
|
;
|
|
|
|
opt_id: ID { $$ = $1; }
|
|
| { $$ = ""; }
|
|
;
|
|
|
|
NUMBER: DIGITS { $$ = strtoul ($1, 0, 0); }
|
|
|
|
%%
|
|
|
|
/*****************************************************************************
|
|
API
|
|
*****************************************************************************/
|
|
|
|
static FILE *the_file;
|
|
static const char *def_filename;
|
|
static int linenumber;
|
|
static def_file *def;
|
|
static int saw_newline;
|
|
|
|
struct directive
|
|
{
|
|
struct directive *next;
|
|
char *name;
|
|
int len;
|
|
};
|
|
|
|
static struct directive *directives = 0;
|
|
|
|
def_file *
|
|
def_file_empty (void)
|
|
{
|
|
def_file *rv = xmalloc (sizeof (def_file));
|
|
memset (rv, 0, sizeof (def_file));
|
|
rv->is_dll = -1;
|
|
rv->base_address = (bfd_vma) -1;
|
|
rv->stack_reserve = rv->stack_commit = -1;
|
|
rv->heap_reserve = rv->heap_commit = -1;
|
|
rv->version_major = rv->version_minor = -1;
|
|
return rv;
|
|
}
|
|
|
|
def_file *
|
|
def_file_parse (const char *filename, def_file *add_to)
|
|
{
|
|
struct directive *d;
|
|
|
|
the_file = fopen (filename, "r");
|
|
def_filename = filename;
|
|
linenumber = 1;
|
|
if (!the_file)
|
|
{
|
|
perror (filename);
|
|
return 0;
|
|
}
|
|
if (add_to)
|
|
{
|
|
def = add_to;
|
|
}
|
|
else
|
|
{
|
|
def = def_file_empty ();
|
|
}
|
|
|
|
saw_newline = 1;
|
|
if (def_parse ())
|
|
{
|
|
def_file_free (def);
|
|
fclose (the_file);
|
|
def_pool_free ();
|
|
return 0;
|
|
}
|
|
|
|
fclose (the_file);
|
|
|
|
while ((d = directives) != NULL)
|
|
{
|
|
#if TRACE
|
|
printf ("Adding directive %08x `%s'\n", d->name, d->name);
|
|
#endif
|
|
def_file_add_directive (def, d->name, d->len);
|
|
directives = d->next;
|
|
free (d->name);
|
|
free (d);
|
|
}
|
|
def_pool_free ();
|
|
|
|
return def;
|
|
}
|
|
|
|
void
|
|
def_file_free (def_file *fdef)
|
|
{
|
|
int i;
|
|
|
|
if (!fdef)
|
|
return;
|
|
if (fdef->name)
|
|
free (fdef->name);
|
|
if (fdef->description)
|
|
free (fdef->description);
|
|
|
|
if (fdef->section_defs)
|
|
{
|
|
for (i = 0; i < fdef->num_section_defs; i++)
|
|
{
|
|
if (fdef->section_defs[i].name)
|
|
free (fdef->section_defs[i].name);
|
|
if (fdef->section_defs[i].class)
|
|
free (fdef->section_defs[i].class);
|
|
}
|
|
free (fdef->section_defs);
|
|
}
|
|
|
|
if (fdef->exports)
|
|
{
|
|
for (i = 0; i < fdef->num_exports; i++)
|
|
{
|
|
if (fdef->exports[i].internal_name
|
|
&& fdef->exports[i].internal_name != fdef->exports[i].name)
|
|
free (fdef->exports[i].internal_name);
|
|
if (fdef->exports[i].name)
|
|
free (fdef->exports[i].name);
|
|
if (fdef->exports[i].its_name)
|
|
free (fdef->exports[i].its_name);
|
|
}
|
|
free (fdef->exports);
|
|
}
|
|
|
|
if (fdef->imports)
|
|
{
|
|
for (i = 0; i < fdef->num_imports; i++)
|
|
{
|
|
if (fdef->imports[i].internal_name
|
|
&& fdef->imports[i].internal_name != fdef->imports[i].name)
|
|
free (fdef->imports[i].internal_name);
|
|
if (fdef->imports[i].name)
|
|
free (fdef->imports[i].name);
|
|
if (fdef->imports[i].its_name)
|
|
free (fdef->imports[i].its_name);
|
|
}
|
|
free (fdef->imports);
|
|
}
|
|
|
|
while (fdef->modules)
|
|
{
|
|
def_file_module *m = fdef->modules;
|
|
|
|
fdef->modules = fdef->modules->next;
|
|
free (m);
|
|
}
|
|
|
|
while (fdef->aligncomms)
|
|
{
|
|
def_file_aligncomm *c = fdef->aligncomms;
|
|
|
|
fdef->aligncomms = fdef->aligncomms->next;
|
|
free (c->symbol_name);
|
|
free (c);
|
|
}
|
|
|
|
free (fdef);
|
|
}
|
|
|
|
#ifdef DEF_FILE_PRINT
|
|
void
|
|
def_file_print (FILE *file, def_file *fdef)
|
|
{
|
|
int i;
|
|
|
|
fprintf (file, ">>>> def_file at 0x%08x\n", fdef);
|
|
if (fdef->name)
|
|
fprintf (file, " name: %s\n", fdef->name ? fdef->name : "(unspecified)");
|
|
if (fdef->is_dll != -1)
|
|
fprintf (file, " is dll: %s\n", fdef->is_dll ? "yes" : "no");
|
|
if (fdef->base_address != (bfd_vma) -1)
|
|
fprintf (file, " base address: 0x%08x\n", fdef->base_address);
|
|
if (fdef->description)
|
|
fprintf (file, " description: `%s'\n", fdef->description);
|
|
if (fdef->stack_reserve != -1)
|
|
fprintf (file, " stack reserve: 0x%08x\n", fdef->stack_reserve);
|
|
if (fdef->stack_commit != -1)
|
|
fprintf (file, " stack commit: 0x%08x\n", fdef->stack_commit);
|
|
if (fdef->heap_reserve != -1)
|
|
fprintf (file, " heap reserve: 0x%08x\n", fdef->heap_reserve);
|
|
if (fdef->heap_commit != -1)
|
|
fprintf (file, " heap commit: 0x%08x\n", fdef->heap_commit);
|
|
|
|
if (fdef->num_section_defs > 0)
|
|
{
|
|
fprintf (file, " section defs:\n");
|
|
|
|
for (i = 0; i < fdef->num_section_defs; i++)
|
|
{
|
|
fprintf (file, " name: `%s', class: `%s', flags:",
|
|
fdef->section_defs[i].name, fdef->section_defs[i].class);
|
|
if (fdef->section_defs[i].flag_read)
|
|
fprintf (file, " R");
|
|
if (fdef->section_defs[i].flag_write)
|
|
fprintf (file, " W");
|
|
if (fdef->section_defs[i].flag_execute)
|
|
fprintf (file, " X");
|
|
if (fdef->section_defs[i].flag_shared)
|
|
fprintf (file, " S");
|
|
fprintf (file, "\n");
|
|
}
|
|
}
|
|
|
|
if (fdef->num_exports > 0)
|
|
{
|
|
fprintf (file, " exports:\n");
|
|
|
|
for (i = 0; i < fdef->num_exports; i++)
|
|
{
|
|
fprintf (file, " name: `%s', int: `%s', ordinal: %d, flags:",
|
|
fdef->exports[i].name, fdef->exports[i].internal_name,
|
|
fdef->exports[i].ordinal);
|
|
if (fdef->exports[i].flag_private)
|
|
fprintf (file, " P");
|
|
if (fdef->exports[i].flag_constant)
|
|
fprintf (file, " C");
|
|
if (fdef->exports[i].flag_noname)
|
|
fprintf (file, " N");
|
|
if (fdef->exports[i].flag_data)
|
|
fprintf (file, " D");
|
|
fprintf (file, "\n");
|
|
}
|
|
}
|
|
|
|
if (fdef->num_imports > 0)
|
|
{
|
|
fprintf (file, " imports:\n");
|
|
|
|
for (i = 0; i < fdef->num_imports; i++)
|
|
{
|
|
fprintf (file, " int: %s, from: `%s', name: `%s', ordinal: %d\n",
|
|
fdef->imports[i].internal_name,
|
|
fdef->imports[i].module,
|
|
fdef->imports[i].name,
|
|
fdef->imports[i].ordinal);
|
|
}
|
|
}
|
|
|
|
if (fdef->version_major != -1)
|
|
fprintf (file, " version: %d.%d\n", fdef->version_major, fdef->version_minor);
|
|
|
|
fprintf (file, "<<<< def_file at 0x%08x\n", fdef);
|
|
}
|
|
#endif
|
|
|
|
/* Helper routine to check for identity of string pointers,
|
|
which might be NULL. */
|
|
|
|
static int
|
|
are_names_equal (const char *s1, const char *s2)
|
|
{
|
|
if (!s1 && !s2)
|
|
return 0;
|
|
if (!s1 || !s2)
|
|
return (!s1 ? -1 : 1);
|
|
return strcmp (s1, s2);
|
|
}
|
|
|
|
static int
|
|
cmp_export_elem (const def_file_export *e, const char *ex_name,
|
|
const char *in_name, const char *its_name,
|
|
int ord)
|
|
{
|
|
int r;
|
|
|
|
if ((r = are_names_equal (ex_name, e->name)) != 0)
|
|
return r;
|
|
if ((r = are_names_equal (in_name, e->internal_name)) != 0)
|
|
return r;
|
|
if ((r = are_names_equal (its_name, e->its_name)) != 0)
|
|
return r;
|
|
return (ord - e->ordinal);
|
|
}
|
|
|
|
/* Search the position of the identical element, or returns the position
|
|
of the next higher element. If last valid element is smaller, then MAX
|
|
is returned. */
|
|
|
|
static int
|
|
find_export_in_list (def_file_export *b, int max,
|
|
const char *ex_name, const char *in_name,
|
|
const char *its_name, int ord, int *is_ident)
|
|
{
|
|
int e, l, r, p;
|
|
|
|
*is_ident = 0;
|
|
if (!max)
|
|
return 0;
|
|
if ((e = cmp_export_elem (b, ex_name, in_name, its_name, ord)) <= 0)
|
|
return 0;
|
|
if (max == 1)
|
|
return 1;
|
|
if ((e = cmp_export_elem (b + (max - 1), ex_name, in_name, its_name, ord)) > 0)
|
|
return max;
|
|
else if (!e || max == 2)
|
|
return max - 1;
|
|
l = 0; r = max - 1;
|
|
while (l < r)
|
|
{
|
|
p = (l + r) / 2;
|
|
e = cmp_export_elem (b + p, ex_name, in_name, its_name, ord);
|
|
if (!e)
|
|
{
|
|
*is_ident = 1;
|
|
return p;
|
|
}
|
|
else if (e < 0)
|
|
r = p - 1;
|
|
else if (e > 0)
|
|
l = p + 1;
|
|
}
|
|
if ((e = cmp_export_elem (b + l, ex_name, in_name, its_name, ord)) > 0)
|
|
++l;
|
|
else if (!e)
|
|
*is_ident = 1;
|
|
return l;
|
|
}
|
|
|
|
def_file_export *
|
|
def_file_add_export (def_file *fdef,
|
|
const char *external_name,
|
|
const char *internal_name,
|
|
int ordinal,
|
|
const char *its_name,
|
|
int *is_dup)
|
|
{
|
|
def_file_export *e;
|
|
int pos;
|
|
int max_exports = ROUND_UP(fdef->num_exports, 32);
|
|
|
|
if (internal_name && !external_name)
|
|
external_name = internal_name;
|
|
if (external_name && !internal_name)
|
|
internal_name = external_name;
|
|
|
|
/* We need to avoid duplicates. */
|
|
*is_dup = 0;
|
|
pos = find_export_in_list (fdef->exports, fdef->num_exports,
|
|
external_name, internal_name,
|
|
its_name, ordinal, is_dup);
|
|
|
|
if (*is_dup != 0)
|
|
return (fdef->exports + pos);
|
|
|
|
if (fdef->num_exports >= max_exports)
|
|
{
|
|
max_exports = ROUND_UP(fdef->num_exports + 1, 32);
|
|
if (fdef->exports)
|
|
fdef->exports = xrealloc (fdef->exports,
|
|
max_exports * sizeof (def_file_export));
|
|
else
|
|
fdef->exports = xmalloc (max_exports * sizeof (def_file_export));
|
|
}
|
|
|
|
e = fdef->exports + pos;
|
|
if (pos != fdef->num_exports)
|
|
memmove (&e[1], e, (sizeof (def_file_export) * (fdef->num_exports - pos)));
|
|
memset (e, 0, sizeof (def_file_export));
|
|
e->name = xstrdup (external_name);
|
|
e->internal_name = xstrdup (internal_name);
|
|
e->its_name = (its_name ? xstrdup (its_name) : NULL);
|
|
e->ordinal = ordinal;
|
|
fdef->num_exports++;
|
|
return e;
|
|
}
|
|
|
|
def_file_module *
|
|
def_get_module (def_file *fdef, const char *name)
|
|
{
|
|
def_file_module *s;
|
|
|
|
for (s = fdef->modules; s; s = s->next)
|
|
if (strcmp (s->name, name) == 0)
|
|
return s;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static def_file_module *
|
|
def_stash_module (def_file *fdef, const char *name)
|
|
{
|
|
def_file_module *s;
|
|
|
|
if ((s = def_get_module (fdef, name)) != NULL)
|
|
return s;
|
|
s = xmalloc (sizeof (def_file_module) + strlen (name));
|
|
s->next = fdef->modules;
|
|
fdef->modules = s;
|
|
s->user_data = 0;
|
|
strcpy (s->name, name);
|
|
return s;
|
|
}
|
|
|
|
static int
|
|
cmp_import_elem (const def_file_import *e, const char *ex_name,
|
|
const char *in_name, const char *module,
|
|
int ord)
|
|
{
|
|
int r;
|
|
|
|
if ((r = are_names_equal (ex_name, e->name)) != 0)
|
|
return r;
|
|
if ((r = are_names_equal (in_name, e->internal_name)) != 0)
|
|
return r;
|
|
if (ord != e->ordinal)
|
|
return (ord < e->ordinal ? -1 : 1);
|
|
return are_names_equal (module, (e->module ? e->module->name : NULL));
|
|
}
|
|
|
|
/* Search the position of the identical element, or returns the position
|
|
of the next higher element. If last valid element is smaller, then MAX
|
|
is returned. */
|
|
|
|
static int
|
|
find_import_in_list (def_file_import *b, int max,
|
|
const char *ex_name, const char *in_name,
|
|
const char *module, int ord, int *is_ident)
|
|
{
|
|
int e, l, r, p;
|
|
|
|
*is_ident = 0;
|
|
if (!max)
|
|
return 0;
|
|
if ((e = cmp_import_elem (b, ex_name, in_name, module, ord)) <= 0)
|
|
return 0;
|
|
if (max == 1)
|
|
return 1;
|
|
if ((e = cmp_import_elem (b + (max - 1), ex_name, in_name, module, ord)) > 0)
|
|
return max;
|
|
else if (!e || max == 2)
|
|
return max - 1;
|
|
l = 0; r = max - 1;
|
|
while (l < r)
|
|
{
|
|
p = (l + r) / 2;
|
|
e = cmp_import_elem (b + p, ex_name, in_name, module, ord);
|
|
if (!e)
|
|
{
|
|
*is_ident = 1;
|
|
return p;
|
|
}
|
|
else if (e < 0)
|
|
r = p - 1;
|
|
else if (e > 0)
|
|
l = p + 1;
|
|
}
|
|
if ((e = cmp_import_elem (b + l, ex_name, in_name, module, ord)) > 0)
|
|
++l;
|
|
else if (!e)
|
|
*is_ident = 1;
|
|
return l;
|
|
}
|
|
|
|
def_file_import *
|
|
def_file_add_import (def_file *fdef,
|
|
const char *name,
|
|
const char *module,
|
|
int ordinal,
|
|
const char *internal_name,
|
|
const char *its_name,
|
|
int *is_dup)
|
|
{
|
|
def_file_import *i;
|
|
int pos;
|
|
int max_imports = ROUND_UP (fdef->num_imports, 16);
|
|
|
|
/* We need to avoid here duplicates. */
|
|
*is_dup = 0;
|
|
pos = find_import_in_list (fdef->imports, fdef->num_imports,
|
|
name,
|
|
(!internal_name ? name : internal_name),
|
|
module, ordinal, is_dup);
|
|
if (*is_dup != 0)
|
|
return fdef->imports + pos;
|
|
|
|
if (fdef->num_imports >= max_imports)
|
|
{
|
|
max_imports = ROUND_UP (fdef->num_imports+1, 16);
|
|
|
|
if (fdef->imports)
|
|
fdef->imports = xrealloc (fdef->imports,
|
|
max_imports * sizeof (def_file_import));
|
|
else
|
|
fdef->imports = xmalloc (max_imports * sizeof (def_file_import));
|
|
}
|
|
i = fdef->imports + pos;
|
|
if (pos != fdef->num_imports)
|
|
memmove (&i[1], i, (sizeof (def_file_import) * (fdef->num_imports - pos)));
|
|
memset (i, 0, sizeof (def_file_import));
|
|
if (name)
|
|
i->name = xstrdup (name);
|
|
if (module)
|
|
i->module = def_stash_module (fdef, module);
|
|
i->ordinal = ordinal;
|
|
if (internal_name)
|
|
i->internal_name = xstrdup (internal_name);
|
|
else
|
|
i->internal_name = i->name;
|
|
i->its_name = (its_name ? xstrdup (its_name) : NULL);
|
|
fdef->num_imports++;
|
|
|
|
return i;
|
|
}
|
|
|
|
struct
|
|
{
|
|
char *param;
|
|
int token;
|
|
}
|
|
diropts[] =
|
|
{
|
|
{ "-heap", HEAPSIZE },
|
|
{ "-stack", STACKSIZE_K },
|
|
{ "-attr", SECTIONS },
|
|
{ "-export", EXPORTS },
|
|
{ "-aligncomm", ALIGNCOMM },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
void
|
|
def_file_add_directive (def_file *my_def, const char *param, int len)
|
|
{
|
|
def_file *save_def = def;
|
|
const char *pend = param + len;
|
|
char * tend = (char *) param;
|
|
int i;
|
|
|
|
def = my_def;
|
|
|
|
while (param < pend)
|
|
{
|
|
while (param < pend
|
|
&& (ISSPACE (*param) || *param == '\n' || *param == 0))
|
|
param++;
|
|
|
|
if (param == pend)
|
|
break;
|
|
|
|
/* Scan forward until we encounter any of:
|
|
- the end of the buffer
|
|
- the start of a new option
|
|
- a newline seperating options
|
|
- a NUL seperating options. */
|
|
for (tend = (char *) (param + 1);
|
|
(tend < pend
|
|
&& !(ISSPACE (tend[-1]) && *tend == '-')
|
|
&& *tend != '\n' && *tend != 0);
|
|
tend++)
|
|
;
|
|
|
|
for (i = 0; diropts[i].param; i++)
|
|
{
|
|
len = strlen (diropts[i].param);
|
|
|
|
if (tend - param >= len
|
|
&& strncmp (param, diropts[i].param, len) == 0
|
|
&& (param[len] == ':' || param[len] == ' '))
|
|
{
|
|
lex_parse_string_end = tend;
|
|
lex_parse_string = param + len + 1;
|
|
lex_forced_token = diropts[i].token;
|
|
saw_newline = 0;
|
|
if (def_parse ())
|
|
continue;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!diropts[i].param)
|
|
{
|
|
char saved;
|
|
|
|
saved = * tend;
|
|
* tend = 0;
|
|
/* xgettext:c-format */
|
|
einfo (_("Warning: .drectve `%s' unrecognized\n"), param);
|
|
* tend = saved;
|
|
}
|
|
|
|
lex_parse_string = 0;
|
|
param = tend;
|
|
}
|
|
|
|
def = save_def;
|
|
def_pool_free ();
|
|
}
|
|
|
|
/* Parser Callbacks. */
|
|
|
|
static void
|
|
def_image_name (const char *name, int base, int is_dll)
|
|
{
|
|
/* If a LIBRARY or NAME statement is specified without a name, there is nothing
|
|
to do here. We retain the output filename specified on command line. */
|
|
if (*name)
|
|
{
|
|
const char* image_name = lbasename (name);
|
|
|
|
if (image_name != name)
|
|
einfo ("%s:%d: Warning: path components stripped from %s, '%s'\n",
|
|
def_filename, linenumber, is_dll ? "LIBRARY" : "NAME",
|
|
name);
|
|
if (def->name)
|
|
free (def->name);
|
|
/* Append the default suffix, if none specified. */
|
|
if (strchr (image_name, '.') == 0)
|
|
{
|
|
const char * suffix = is_dll ? ".dll" : ".exe";
|
|
|
|
def->name = xmalloc (strlen (image_name) + strlen (suffix) + 1);
|
|
sprintf (def->name, "%s%s", image_name, suffix);
|
|
}
|
|
else
|
|
def->name = xstrdup (image_name);
|
|
}
|
|
|
|
/* Honor a BASE address statement, even if LIBRARY string is empty. */
|
|
def->base_address = base;
|
|
def->is_dll = is_dll;
|
|
}
|
|
|
|
static void
|
|
def_description (const char *text)
|
|
{
|
|
int len = def->description ? strlen (def->description) : 0;
|
|
|
|
len += strlen (text) + 1;
|
|
if (def->description)
|
|
{
|
|
def->description = xrealloc (def->description, len);
|
|
strcat (def->description, text);
|
|
}
|
|
else
|
|
{
|
|
def->description = xmalloc (len);
|
|
strcpy (def->description, text);
|
|
}
|
|
}
|
|
|
|
static void
|
|
def_stacksize (int reserve, int commit)
|
|
{
|
|
def->stack_reserve = reserve;
|
|
def->stack_commit = commit;
|
|
}
|
|
|
|
static void
|
|
def_heapsize (int reserve, int commit)
|
|
{
|
|
def->heap_reserve = reserve;
|
|
def->heap_commit = commit;
|
|
}
|
|
|
|
static void
|
|
def_section (const char *name, int attr)
|
|
{
|
|
def_file_section *s;
|
|
int max_sections = ROUND_UP (def->num_section_defs, 4);
|
|
|
|
if (def->num_section_defs >= max_sections)
|
|
{
|
|
max_sections = ROUND_UP (def->num_section_defs+1, 4);
|
|
|
|
if (def->section_defs)
|
|
def->section_defs = xrealloc (def->section_defs,
|
|
max_sections * sizeof (def_file_import));
|
|
else
|
|
def->section_defs = xmalloc (max_sections * sizeof (def_file_import));
|
|
}
|
|
s = def->section_defs + def->num_section_defs;
|
|
memset (s, 0, sizeof (def_file_section));
|
|
s->name = xstrdup (name);
|
|
if (attr & 1)
|
|
s->flag_read = 1;
|
|
if (attr & 2)
|
|
s->flag_write = 1;
|
|
if (attr & 4)
|
|
s->flag_execute = 1;
|
|
if (attr & 8)
|
|
s->flag_shared = 1;
|
|
|
|
def->num_section_defs++;
|
|
}
|
|
|
|
static void
|
|
def_section_alt (const char *name, const char *attr)
|
|
{
|
|
int aval = 0;
|
|
|
|
for (; *attr; attr++)
|
|
{
|
|
switch (*attr)
|
|
{
|
|
case 'R':
|
|
case 'r':
|
|
aval |= 1;
|
|
break;
|
|
case 'W':
|
|
case 'w':
|
|
aval |= 2;
|
|
break;
|
|
case 'X':
|
|
case 'x':
|
|
aval |= 4;
|
|
break;
|
|
case 'S':
|
|
case 's':
|
|
aval |= 8;
|
|
break;
|
|
}
|
|
}
|
|
def_section (name, aval);
|
|
}
|
|
|
|
static void
|
|
def_exports (const char *external_name,
|
|
const char *internal_name,
|
|
int ordinal,
|
|
int flags,
|
|
const char *its_name)
|
|
{
|
|
def_file_export *dfe;
|
|
int is_dup = 0;
|
|
|
|
if (!internal_name && external_name)
|
|
internal_name = external_name;
|
|
#if TRACE
|
|
printf ("def_exports, ext=%s int=%s\n", external_name, internal_name);
|
|
#endif
|
|
|
|
dfe = def_file_add_export (def, external_name, internal_name, ordinal,
|
|
its_name, &is_dup);
|
|
|
|
/* We might check here for flag redefinition and warn. For now we
|
|
ignore duplicates silently. */
|
|
if (is_dup)
|
|
return;
|
|
|
|
if (flags & 1)
|
|
dfe->flag_noname = 1;
|
|
if (flags & 2)
|
|
dfe->flag_constant = 1;
|
|
if (flags & 4)
|
|
dfe->flag_data = 1;
|
|
if (flags & 8)
|
|
dfe->flag_private = 1;
|
|
}
|
|
|
|
static void
|
|
def_import (const char *internal_name,
|
|
const char *module,
|
|
const char *dllext,
|
|
const char *name,
|
|
int ordinal,
|
|
const char *its_name)
|
|
{
|
|
char *buf = 0;
|
|
const char *ext = dllext ? dllext : "dll";
|
|
int is_dup = 0;
|
|
|
|
buf = xmalloc (strlen (module) + strlen (ext) + 2);
|
|
sprintf (buf, "%s.%s", module, ext);
|
|
module = buf;
|
|
|
|
def_file_add_import (def, name, module, ordinal, internal_name, its_name,
|
|
&is_dup);
|
|
free (buf);
|
|
}
|
|
|
|
static void
|
|
def_version (int major, int minor)
|
|
{
|
|
def->version_major = major;
|
|
def->version_minor = minor;
|
|
}
|
|
|
|
static void
|
|
def_directive (char *str)
|
|
{
|
|
struct directive *d = xmalloc (sizeof (struct directive));
|
|
|
|
d->next = directives;
|
|
directives = d;
|
|
d->name = xstrdup (str);
|
|
d->len = strlen (str);
|
|
}
|
|
|
|
static void
|
|
def_aligncomm (char *str, int align)
|
|
{
|
|
def_file_aligncomm *c, *p;
|
|
|
|
p = NULL;
|
|
c = def->aligncomms;
|
|
while (c != NULL)
|
|
{
|
|
int e = strcmp (c->symbol_name, str);
|
|
if (!e)
|
|
{
|
|
/* Not sure if we want to allow here duplicates with
|
|
different alignments, but for now we keep them. */
|
|
e = (int) c->alignment - align;
|
|
if (!e)
|
|
return;
|
|
}
|
|
if (e > 0)
|
|
break;
|
|
c = (p = c)->next;
|
|
}
|
|
|
|
c = xmalloc (sizeof (def_file_aligncomm));
|
|
c->symbol_name = xstrdup (str);
|
|
c->alignment = (unsigned int) align;
|
|
if (!p)
|
|
{
|
|
c->next = def->aligncomms;
|
|
def->aligncomms = c;
|
|
}
|
|
else
|
|
{
|
|
c->next = p->next;
|
|
p->next = c;
|
|
}
|
|
}
|
|
|
|
static int
|
|
def_error (const char *err)
|
|
{
|
|
einfo ("%P: %s:%d: %s\n",
|
|
def_filename ? def_filename : "<unknown-file>", linenumber, err);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Lexical Scanner. */
|
|
|
|
#undef TRACE
|
|
#define TRACE 0
|
|
|
|
/* Never freed, but always reused as needed, so no real leak. */
|
|
static char *buffer = 0;
|
|
static int buflen = 0;
|
|
static int bufptr = 0;
|
|
|
|
static void
|
|
put_buf (char c)
|
|
{
|
|
if (bufptr == buflen)
|
|
{
|
|
buflen += 50; /* overly reasonable, eh? */
|
|
if (buffer)
|
|
buffer = xrealloc (buffer, buflen + 1);
|
|
else
|
|
buffer = xmalloc (buflen + 1);
|
|
}
|
|
buffer[bufptr++] = c;
|
|
buffer[bufptr] = 0; /* not optimal, but very convenient. */
|
|
}
|
|
|
|
static struct
|
|
{
|
|
char *name;
|
|
int token;
|
|
}
|
|
tokens[] =
|
|
{
|
|
{ "BASE", BASE },
|
|
{ "CODE", CODE },
|
|
{ "CONSTANT", CONSTANTU },
|
|
{ "constant", CONSTANTL },
|
|
{ "DATA", DATAU },
|
|
{ "data", DATAL },
|
|
{ "DESCRIPTION", DESCRIPTION },
|
|
{ "DIRECTIVE", DIRECTIVE },
|
|
{ "EXECUTE", EXECUTE },
|
|
{ "EXPORTS", EXPORTS },
|
|
{ "HEAPSIZE", HEAPSIZE },
|
|
{ "IMPORTS", IMPORTS },
|
|
{ "LIBRARY", LIBRARY },
|
|
{ "NAME", NAME },
|
|
{ "NONAME", NONAMEU },
|
|
{ "noname", NONAMEL },
|
|
{ "PRIVATE", PRIVATEU },
|
|
{ "private", PRIVATEL },
|
|
{ "READ", READ },
|
|
{ "SECTIONS", SECTIONS },
|
|
{ "SEGMENTS", SECTIONS },
|
|
{ "SHARED", SHARED },
|
|
{ "STACKSIZE", STACKSIZE_K },
|
|
{ "VERSION", VERSIONK },
|
|
{ "WRITE", WRITE },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static int
|
|
def_getc (void)
|
|
{
|
|
int rv;
|
|
|
|
if (lex_parse_string)
|
|
{
|
|
if (lex_parse_string >= lex_parse_string_end)
|
|
rv = EOF;
|
|
else
|
|
rv = *lex_parse_string++;
|
|
}
|
|
else
|
|
{
|
|
rv = fgetc (the_file);
|
|
}
|
|
if (rv == '\n')
|
|
saw_newline = 1;
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
def_ungetc (int c)
|
|
{
|
|
if (lex_parse_string)
|
|
{
|
|
lex_parse_string--;
|
|
return c;
|
|
}
|
|
else
|
|
return ungetc (c, the_file);
|
|
}
|
|
|
|
static int
|
|
def_lex (void)
|
|
{
|
|
int c, i, q;
|
|
|
|
if (lex_forced_token)
|
|
{
|
|
i = lex_forced_token;
|
|
lex_forced_token = 0;
|
|
#if TRACE
|
|
printf ("lex: forcing token %d\n", i);
|
|
#endif
|
|
return i;
|
|
}
|
|
|
|
c = def_getc ();
|
|
|
|
/* Trim leading whitespace. */
|
|
while (c != EOF && (c == ' ' || c == '\t') && saw_newline)
|
|
c = def_getc ();
|
|
|
|
if (c == EOF)
|
|
{
|
|
#if TRACE
|
|
printf ("lex: EOF\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (saw_newline && c == ';')
|
|
{
|
|
do
|
|
{
|
|
c = def_getc ();
|
|
}
|
|
while (c != EOF && c != '\n');
|
|
if (c == '\n')
|
|
return def_lex ();
|
|
return 0;
|
|
}
|
|
|
|
/* Must be something else. */
|
|
saw_newline = 0;
|
|
|
|
if (ISDIGIT (c))
|
|
{
|
|
bufptr = 0;
|
|
while (c != EOF && (ISXDIGIT (c) || (c == 'x')))
|
|
{
|
|
put_buf (c);
|
|
c = def_getc ();
|
|
}
|
|
if (c != EOF)
|
|
def_ungetc (c);
|
|
yylval.digits = def_pool_strdup (buffer);
|
|
#if TRACE
|
|
printf ("lex: `%s' returns DIGITS\n", buffer);
|
|
#endif
|
|
return DIGITS;
|
|
}
|
|
|
|
if (ISALPHA (c) || strchr ("$:-_?@", c))
|
|
{
|
|
bufptr = 0;
|
|
q = c;
|
|
put_buf (c);
|
|
c = def_getc ();
|
|
|
|
if (q == '@')
|
|
{
|
|
if (ISBLANK (c) ) /* '@' followed by whitespace. */
|
|
return (q);
|
|
else if (ISDIGIT (c)) /* '@' followed by digit. */
|
|
{
|
|
def_ungetc (c);
|
|
return (q);
|
|
}
|
|
#if TRACE
|
|
printf ("lex: @ returns itself\n");
|
|
#endif
|
|
}
|
|
|
|
while (c != EOF && (ISALNUM (c) || strchr ("$:-_?/@<>", c)))
|
|
{
|
|
put_buf (c);
|
|
c = def_getc ();
|
|
}
|
|
if (c != EOF)
|
|
def_ungetc (c);
|
|
if (ISALPHA (q)) /* Check for tokens. */
|
|
{
|
|
for (i = 0; tokens[i].name; i++)
|
|
if (strcmp (tokens[i].name, buffer) == 0)
|
|
{
|
|
#if TRACE
|
|
printf ("lex: `%s' is a string token\n", buffer);
|
|
#endif
|
|
return tokens[i].token;
|
|
}
|
|
}
|
|
#if TRACE
|
|
printf ("lex: `%s' returns ID\n", buffer);
|
|
#endif
|
|
yylval.id = def_pool_strdup (buffer);
|
|
return ID;
|
|
}
|
|
|
|
if (c == '\'' || c == '"')
|
|
{
|
|
q = c;
|
|
c = def_getc ();
|
|
bufptr = 0;
|
|
|
|
while (c != EOF && c != q)
|
|
{
|
|
put_buf (c);
|
|
c = def_getc ();
|
|
}
|
|
yylval.id = def_pool_strdup (buffer);
|
|
#if TRACE
|
|
printf ("lex: `%s' returns ID\n", buffer);
|
|
#endif
|
|
return ID;
|
|
}
|
|
|
|
if ( c == '=')
|
|
{
|
|
c = def_getc ();
|
|
if (c == '=')
|
|
{
|
|
#if TRACE
|
|
printf ("lex: `==' returns EQUAL\n");
|
|
#endif
|
|
return EQUAL;
|
|
}
|
|
def_ungetc (c);
|
|
#if TRACE
|
|
printf ("lex: `=' returns itself\n");
|
|
#endif
|
|
return '=';
|
|
}
|
|
if (c == '.' || c == ',')
|
|
{
|
|
#if TRACE
|
|
printf ("lex: `%c' returns itself\n", c);
|
|
#endif
|
|
return c;
|
|
}
|
|
|
|
if (c == '\n')
|
|
{
|
|
linenumber++;
|
|
saw_newline = 1;
|
|
}
|
|
|
|
/*printf ("lex: 0x%02x ignored\n", c); */
|
|
return def_lex ();
|
|
}
|
|
|
|
static char *
|
|
def_pool_alloc (size_t sz)
|
|
{
|
|
def_pool_str *e;
|
|
|
|
e = (def_pool_str *) xmalloc (sizeof (def_pool_str) + sz);
|
|
e->next = pool_strs;
|
|
pool_strs = e;
|
|
return e->data;
|
|
}
|
|
|
|
static char *
|
|
def_pool_strdup (const char *str)
|
|
{
|
|
char *s;
|
|
size_t len;
|
|
if (!str)
|
|
return NULL;
|
|
len = strlen (str) + 1;
|
|
s = def_pool_alloc (len);
|
|
memcpy (s, str, len);
|
|
return s;
|
|
}
|
|
|
|
static void
|
|
def_pool_free (void)
|
|
{
|
|
def_pool_str *p;
|
|
while ((p = pool_strs) != NULL)
|
|
{
|
|
pool_strs = p->next;
|
|
free (p);
|
|
}
|
|
}
|