OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-stable/] [gcc-4.5.1/] [lto-plugin/] [lto-plugin.c] - Blame information for rev 826

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 277 jeremybenn
/* LTO plugin for gold.
2
   Copyright (C) 2009 Free Software Foundation, Inc.
3
   Contributed by Rafael Avila de Espindola (espindola@google.com).
4
 
5
This program is free software; you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation; either version 3, or (at your option)
8
any later version.
9
 
10
This program is distributed in the hope that it will be useful, but
11
WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
General Public License for more details.
14
 
15
You should have received a copy of the GNU General Public License
16
along with this program; see the file COPYING3.  If not see
17
<http://www.gnu.org/licenses/>.  */
18
 
19
/* The plugin has only one external function: onload. Gold passes it an array of
20
   function that the plugin uses to communicate back to gold.
21
 
22
   With the functions provided by gold, the plugin can be notified when
23
   gold first analyzes a file and pass a symbol table back to gold. The plugin
24
   is also notified when all symbols have been read and it is time to generate
25
   machine code for the necessary symbols.
26
 
27
   More information at http://gcc.gnu.org/wiki/whopr/driver.
28
 
29
   This plugin should be passed the lto-wrapper options and will forward them.
30
   It also has 2 options of its own:
31
   -debug: Print the command line used to run lto-wrapper.
32
   -nop: Instead of running lto-wrapper, pass the original to the plugin. This
33
   only works if the input files are hybrid.  */
34
 
35
#include <assert.h>
36
#include <string.h>
37
#include <stdlib.h>
38
#include <stdio.h>
39
#include <inttypes.h>
40
#include <sys/stat.h>
41
#include <unistd.h>
42
#include <fcntl.h>
43
#include <sys/types.h>
44
#include <sys/wait.h>
45
#include <stdbool.h>
46
#include <libiberty.h>
47
 
48
/* The presence of gelf.h is checked by the toplevel configure script.  */
49
#include <gelf.h>
50
 
51
#include "plugin-api.h"
52
#include "../gcc/lto/common.h"
53
 
54
/* The part of the symbol table the plugin has to keep track of. Note that we
55
   must keep SYMS until all_symbols_read is called to give the linker time to
56
   copy the symbol information. */
57
 
58
struct plugin_symtab
59
{
60
  int nsyms;
61
  uint32_t *slots;
62
  struct ld_plugin_symbol *syms;
63
};
64
 
65
/* All that we have to remember about a file. */
66
 
67
struct plugin_file_info
68
{
69
  char *name;
70
  void *handle;
71
  struct plugin_symtab symtab;
72
};
73
 
74
 
75
static char *arguments_file_name;
76
static ld_plugin_register_claim_file register_claim_file;
77
static ld_plugin_add_symbols add_symbols;
78
static ld_plugin_register_all_symbols_read register_all_symbols_read;
79
static ld_plugin_get_symbols get_symbols;
80
static ld_plugin_register_cleanup register_cleanup;
81
static ld_plugin_add_input_file add_input_file;
82
static ld_plugin_add_input_library add_input_library;
83
static ld_plugin_message message;
84
 
85
static struct plugin_file_info *claimed_files = NULL;
86
static unsigned int num_claimed_files = 0;
87
 
88
static char **output_files = NULL;
89
static unsigned int num_output_files = 0;
90
 
91
static char **lto_wrapper_argv;
92
static int lto_wrapper_num_args;
93
 
94
static char **pass_through_items = NULL;
95
static unsigned int num_pass_through_items;
96
 
97
static bool debug;
98
static bool nop;
99
static char *resolution_file = NULL;
100
 
101
static void
102
check (bool gate, enum ld_plugin_level level, const char *text)
103
{
104
  if (gate)
105
    return;
106
 
107
  if (message)
108
    message (level, text);
109
  else
110
    {
111
      /* If there is no nicer way to inform the user, fallback to stderr. */
112
      fprintf (stderr, "%s\n", text);
113
      if (level == LDPL_FATAL)
114
        abort ();
115
    }
116
}
117
 
118
/* Parse an entry of the IL symbol table. The data to be parsed is pointed
119
   by P and the result is written in ENTRY. The slot number is stored in SLOT.
120
   Returns the address of the next entry. */
121
 
122
static char *
123
parse_table_entry (char *p, struct ld_plugin_symbol *entry, uint32_t *slot)
124
{
125
  unsigned char t;
126
  enum ld_plugin_symbol_kind translate_kind[] =
127
    {
128
      LDPK_DEF,
129
      LDPK_WEAKDEF,
130
      LDPK_UNDEF,
131
      LDPK_WEAKUNDEF,
132
      LDPK_COMMON
133
    };
134
 
135
  enum ld_plugin_symbol_visibility translate_visibility[] =
136
    {
137
      LDPV_DEFAULT,
138
      LDPV_PROTECTED,
139
      LDPV_INTERNAL,
140
      LDPV_HIDDEN
141
    };
142
 
143
  entry->name = strdup (p);
144
  while (*p)
145
    p++;
146
  p++;
147
 
148
  entry->version = NULL;
149
 
150
  entry->comdat_key = p;
151
  while (*p)
152
    p++;
153
  p++;
154
 
155
  if (strlen (entry->comdat_key) == 0)
156
    entry->comdat_key = NULL;
157
  else
158
    entry->comdat_key = strdup (entry->comdat_key);
159
 
160
  t = *p;
161
  check (t <= 4, LDPL_FATAL, "invalid symbol kind found");
162
  entry->def = translate_kind[t];
163
  p++;
164
 
165
  t = *p;
166
  check (t <= 3, LDPL_FATAL, "invalid symbol visibility found");
167
  entry->visibility = translate_visibility[t];
168
  p++;
169
 
170
  entry->size = *(uint64_t *) p;
171
  p += 8;
172
 
173
  *slot = *(uint32_t *) p;
174
  p += 4;
175
 
176
  entry->resolution = LDPR_UNKNOWN;
177
 
178
  return p;
179
}
180
 
181
/* Return the section in ELF that is named NAME. */
182
 
183
static Elf_Scn *
184
get_section (Elf *elf, const char *name)
185
{
186
  Elf_Scn *section = 0;
187
  GElf_Ehdr header;
188
  GElf_Ehdr *t = gelf_getehdr (elf, &header);
189
  if (t == NULL)
190
    return NULL;
191
  assert (t == &header);
192
 
193
  while ((section = elf_nextscn(elf, section)) != 0)
194
    {
195
      GElf_Shdr shdr;
196
      GElf_Shdr *tshdr = gelf_getshdr (section, &shdr);
197
      const char *t;
198
      assert (tshdr == &shdr);
199
      t = elf_strptr (elf, header.e_shstrndx, shdr.sh_name);
200
      assert (t != NULL);
201
      if (strcmp (t, name) == 0)
202
        return section;
203
    }
204
  return NULL;
205
}
206
 
207
/* Returns the IL symbol table of file ELF. */
208
 
209
static Elf_Data *
210
get_symtab (Elf *elf)
211
{
212
  Elf_Data *data = 0;
213
  Elf_Scn *section = get_section (elf, ".gnu.lto_.symtab");
214
  if (!section)
215
    return NULL;
216
 
217
  data = elf_getdata (section, data);
218
  assert (data);
219
  return data;
220
}
221
 
222
/* Translate the IL symbol table SYMTAB. Write the slots and symbols in OUT. */
223
 
224
static void
225
translate (Elf_Data *symtab, struct plugin_symtab *out)
226
{
227
  uint32_t *slots = NULL;
228
  char *data = symtab->d_buf;
229
  char *end = data + symtab->d_size;
230
  struct ld_plugin_symbol *syms = NULL;
231
  int n = 0;
232
 
233
  while (data < end)
234
    {
235
      n++;
236
      syms = realloc (syms, n * sizeof (struct ld_plugin_symbol));
237
      check (syms, LDPL_FATAL, "could not allocate memory");
238
      slots = realloc (slots, n * sizeof (uint32_t));
239
      check (slots, LDPL_FATAL, "could not allocate memory");
240
      data = parse_table_entry (data, &syms[n - 1], &slots[n - 1]);
241
    }
242
 
243
  out->nsyms = n;
244
  out->syms = syms;
245
  out->slots = slots;
246
}
247
 
248
/* Free all memory that is no longer needed after writing the symbol
249
   resolution. */
250
 
251
static void
252
free_1 (void)
253
{
254
  unsigned int i;
255
  for (i = 0; i < num_claimed_files; i++)
256
    {
257
      struct plugin_file_info *info = &claimed_files[i];
258
      struct plugin_symtab *symtab = &info->symtab;
259
      unsigned int j;
260
      for (j = 0; j < symtab->nsyms; j++)
261
        {
262
          struct ld_plugin_symbol *s = &symtab->syms[j];
263
          free (s->name);
264
          if (s->comdat_key)
265
            free (s->comdat_key);
266
        }
267
      free (symtab->syms);
268
      symtab->syms = NULL;
269
    }
270
}
271
 
272
/* Free all remaining memory. */
273
 
274
static void
275
free_2 (void)
276
{
277
  unsigned int i;
278
  for (i = 0; i < num_claimed_files; i++)
279
    {
280
      struct plugin_file_info *info = &claimed_files[i];
281
      struct plugin_symtab *symtab = &info->symtab;
282
      free (symtab->slots);
283
      free (info->name);
284
    }
285
 
286
  for (i = 0; i < num_output_files; i++)
287
    free (output_files[i]);
288
  free (output_files);
289
 
290
  free (claimed_files);
291
  claimed_files = NULL;
292
  num_claimed_files = 0;
293
 
294
  if (arguments_file_name)
295
    free (arguments_file_name);
296
  arguments_file_name = NULL;
297
 
298
  if (resolution_file)
299
    {
300
      free (resolution_file);
301
      resolution_file = NULL;
302
    }
303
}
304
 
305
/*  Writes the relocations to disk. */
306
 
307
static void
308
write_resolution (void)
309
{
310
  unsigned int i;
311
  FILE *f;
312
 
313
  f = fopen (resolution_file, "w");
314
  check (f, LDPL_FATAL, "could not open file");
315
 
316
  fprintf (f, "%d\n", num_claimed_files);
317
 
318
  for (i = 0; i < num_claimed_files; i++)
319
    {
320
      struct plugin_file_info *info = &claimed_files[i];
321
      struct plugin_symtab *symtab = &info->symtab;
322
      struct ld_plugin_symbol *syms = symtab->syms;
323
      unsigned j;
324
 
325
      get_symbols (info->handle, symtab->nsyms, syms);
326
 
327
      fprintf (f, "%s %d\n", info->name, info->symtab.nsyms);
328
 
329
      for (j = 0; j < info->symtab.nsyms; j++)
330
        {
331
          uint32_t slot = symtab->slots[j];
332
          unsigned int resolution = syms[j].resolution;
333
          fprintf (f, "%d %s %s\n", slot, lto_resolution_str[resolution], syms[j].name);
334
        }
335
    }
336
  fclose (f);
337
}
338
 
339
/* Pass files generated by the lto-wrapper to the linker. FD is lto-wrapper's
340
   stdout. */
341
 
342
static void
343
add_output_files (FILE *f)
344
{
345
  char fname[1000]; /* FIXME: Remove this restriction. */
346
 
347
  for (;;)
348
    {
349
      size_t len;
350
      char *s = fgets (fname, sizeof (fname), f);
351
      if (!s)
352
        break;
353
 
354
      len = strlen (s);
355
      check (s[len - 1] == '\n', LDPL_FATAL, "file name too long");
356
      s[len - 1] = '\0';
357
 
358
      num_output_files++;
359
      output_files = realloc (output_files, num_output_files * sizeof (char *));
360
      output_files[num_output_files - 1] = strdup (s);
361
      add_input_file (output_files[num_output_files - 1]);
362
    }
363
}
364
 
365
/* Execute the lto-wrapper. ARGV[0] is the binary. The rest of ARGV is the
366
   argument list. */
367
 
368
static void
369
exec_lto_wrapper (char *argv[])
370
{
371
  int t, i;
372
  int status;
373
  char *at_args;
374
  FILE *args;
375
  FILE *wrapper_output;
376
  char *new_argv[3];
377
  struct pex_obj *pex;
378
  const char *errmsg;
379
 
380
  /* Write argv to a file to avoid a command line that is too long. */
381
  arguments_file_name = make_temp_file ("");
382
  check (arguments_file_name, LDPL_FATAL,
383
         "Failed to generate a temorary file name");
384
 
385
  args = fopen (arguments_file_name, "w");
386
  check (args, LDPL_FATAL, "could not open arguments file");
387
 
388
  t = writeargv (&argv[1], args);
389
  check (t == 0, LDPL_FATAL, "could not write arguments");
390
  t = fclose (args);
391
  check (t == 0, LDPL_FATAL, "could not close arguments file");
392
 
393
  at_args = concat ("@", arguments_file_name, NULL);
394
  check (at_args, LDPL_FATAL, "could not allocate");
395
 
396
  for (i = 1; argv[i]; i++)
397
    {
398
      char *a = argv[i];
399
      if (a[0] == '-' && a[1] == 'v' && a[2] == '\0')
400
        {
401
          for (i = 0; argv[i]; i++)
402
            fprintf (stderr, "%s ", argv[i]);
403
          fprintf (stderr, "\n");
404
          break;
405
        }
406
    }
407
 
408
  new_argv[0] = argv[0];
409
  new_argv[1] = at_args;
410
  new_argv[2] = NULL;
411
 
412
  if (debug)
413
    {
414
      for (i = 0; new_argv[i]; i++)
415
        fprintf (stderr, "%s ", new_argv[i]);
416
      fprintf (stderr, "\n");
417
    }
418
 
419
 
420
  pex = pex_init (PEX_USE_PIPES, "lto-wrapper", NULL);
421
  check (pex != NULL, LDPL_FATAL, "could not pex_init lto-wrapper");
422
 
423
  errmsg = pex_run (pex, 0, new_argv[0], new_argv, NULL, NULL, &t);
424
  check (errmsg == NULL, LDPL_FATAL, "could not run lto-wrapper");
425
  check (t == 0, LDPL_FATAL, "could not run lto-wrapper");
426
 
427
  wrapper_output = pex_read_output (pex, 0);
428
  check (wrapper_output, LDPL_FATAL, "could not read lto-wrapper output");
429
 
430
  add_output_files (wrapper_output);
431
 
432
  t = pex_get_status (pex, 1, &status);
433
  check (t == 1, LDPL_FATAL, "could not get lto-wrapper exit status");
434
  check (WIFEXITED (status) && WEXITSTATUS (status) == 0, LDPL_FATAL,
435
         "lto-wrapper failed");
436
 
437
  pex_free (pex);
438
 
439
  free (at_args);
440
}
441
 
442
/* Pass the original files back to the linker. */
443
 
444
static void
445
use_original_files (void)
446
{
447
  unsigned i;
448
  for (i = 0; i < num_claimed_files; i++)
449
    {
450
      struct plugin_file_info *info = &claimed_files[i];
451
      add_input_file (info->name);
452
    }
453
}
454
 
455
 
456
/* Called by the linker once all symbols have been read. */
457
 
458
static enum ld_plugin_status
459
all_symbols_read_handler (void)
460
{
461
  unsigned i;
462
  unsigned num_lto_args = num_claimed_files + lto_wrapper_num_args + 2 + 1;
463
  char **lto_argv;
464
  const char **lto_arg_ptr;
465
  if (num_claimed_files == 0)
466
    return LDPS_OK;
467
 
468
  if (nop)
469
    {
470
      use_original_files ();
471
      return LDPS_OK;
472
    }
473
 
474
  lto_argv = (char **) calloc (sizeof (char *), num_lto_args);
475
  lto_arg_ptr = (const char **) lto_argv;
476
  assert (lto_wrapper_argv);
477
 
478
  resolution_file = make_temp_file ("");
479
 
480
  write_resolution ();
481
 
482
  free_1 ();
483
 
484
  for (i = 0; i < lto_wrapper_num_args; i++)
485
    *lto_arg_ptr++ = lto_wrapper_argv[i];
486
 
487
  *lto_arg_ptr++ = "-fresolution";
488
  *lto_arg_ptr++ = resolution_file;
489
 
490
  for (i = 0; i < num_claimed_files; i++)
491
    {
492
      struct plugin_file_info *info = &claimed_files[i];
493
 
494
      *lto_arg_ptr++ = info->name;
495
    }
496
 
497
  *lto_arg_ptr++ = NULL;
498
  exec_lto_wrapper (lto_argv);
499
 
500
  free (lto_argv);
501
 
502
  if (pass_through_items)
503
    {
504
      unsigned int i;
505
      for (i = 0; i < num_pass_through_items; i++)
506
        {
507
          if (strncmp (pass_through_items[i], "-l", 2) == 0)
508
            add_input_library (pass_through_items[i] + 2);
509
          else
510
            add_input_file (pass_through_items[i]);
511
          free (pass_through_items[i]);
512
          pass_through_items[i] = NULL;
513
        }
514
      free (pass_through_items);
515
      pass_through_items = NULL;
516
    }
517
 
518
  return LDPS_OK;
519
}
520
 
521
/* Remove temporary files at the end of the link. */
522
 
523
static enum ld_plugin_status
524
cleanup_handler (void)
525
{
526
  unsigned int i;
527
  int t;
528
 
529
  if (debug)
530
    return LDPS_OK;
531
 
532
  if (arguments_file_name)
533
    {
534
      t = unlink (arguments_file_name);
535
      check (t == 0, LDPL_FATAL, "could not unlink arguments file");
536
    }
537
 
538
  if (resolution_file)
539
    {
540
      t = unlink (resolution_file);
541
      check (t == 0, LDPL_FATAL, "could not unlink resolution file");
542
    }
543
 
544
  for (i = 0; i < num_output_files; i++)
545
    {
546
      t = unlink (output_files[i]);
547
      check (t == 0, LDPL_FATAL, "could not unlink output file");
548
    }
549
 
550
  free_2 ();
551
  return LDPS_OK;
552
}
553
 
554
/* Callback used by gold to check if the plugin will claim FILE. Writes
555
   the result in CLAIMED. */
556
 
557
static enum ld_plugin_status
558
claim_file_handler (const struct ld_plugin_input_file *file, int *claimed)
559
{
560
  enum ld_plugin_status status;
561
  Elf *elf;
562
  struct plugin_file_info lto_file;
563
  Elf_Data *symtab;
564
 
565
  if (file->offset != 0)
566
    {
567
      char *objname;
568
      Elf *archive;
569
      off_t offset;
570
      /* We pass the offset of the actual file, not the archive header. */
571
      int t = asprintf (&objname, "%s@0x%" PRIx64, file->name,
572
                        (int64_t) file->offset);
573
      check (t >= 0, LDPL_FATAL, "asprintf failed");
574
      lto_file.name = objname;
575
 
576
      archive = elf_begin (file->fd, ELF_C_READ, NULL);
577
      check (elf_kind (archive) == ELF_K_AR, LDPL_FATAL,
578
             "Not an archive and offset not 0");
579
 
580
      /* elf_rand expects the offset to point to the ar header, not the
581
         object itself. Subtract the size of the ar header (60 bytes).
582
         We don't uses sizeof (struct ar_hd) to avoid including ar.h */
583
 
584
      offset = file->offset - 60;
585
      check (offset == elf_rand (archive, offset), LDPL_FATAL,
586
             "could not seek in archive");
587
      elf = elf_begin (file->fd, ELF_C_READ, archive);
588
      check (elf != NULL, LDPL_FATAL, "could not find archive member");
589
      elf_end (archive);
590
    }
591
  else
592
    {
593
      lto_file.name = strdup (file->name);
594
      elf = elf_begin (file->fd, ELF_C_READ, NULL);
595
    }
596
  lto_file.handle = file->handle;
597
 
598
  *claimed = 0;
599
 
600
  if (!elf)
601
    goto err;
602
 
603
  symtab = get_symtab (elf);
604
  if (!symtab)
605
    goto err;
606
 
607
  translate (symtab, &lto_file.symtab);
608
 
609
  status = add_symbols (file->handle, lto_file.symtab.nsyms,
610
                        lto_file.symtab.syms);
611
  check (status == LDPS_OK, LDPL_FATAL, "could not add symbols");
612
 
613
  *claimed = 1;
614
  num_claimed_files++;
615
  claimed_files =
616
    realloc (claimed_files,
617
             num_claimed_files * sizeof (struct plugin_file_info));
618
  claimed_files[num_claimed_files - 1] = lto_file;
619
 
620
  goto cleanup;
621
 
622
 err:
623
  free (lto_file.name);
624
 
625
 cleanup:
626
  if (elf)
627
    elf_end (elf);
628
 
629
  return LDPS_OK;
630
}
631
 
632
/* Parse the plugin options. */
633
 
634
static void
635
process_option (const char *option)
636
{
637
  if (strcmp (option, "-debug") == 0)
638
    debug = 1;
639
  else if (strcmp (option, "-nop") == 0)
640
    nop = 1;
641
  else if (!strncmp (option, "-pass-through=", strlen("-pass-through=")))
642
    {
643
      num_pass_through_items++;
644
      pass_through_items = realloc (pass_through_items,
645
                                    num_pass_through_items * sizeof (char *));
646
      pass_through_items[num_pass_through_items - 1] =
647
          strdup (option + strlen ("-pass-through="));
648
    }
649
  else
650
    {
651
      int size;
652
      lto_wrapper_num_args += 1;
653
      size = lto_wrapper_num_args * sizeof (char *);
654
      lto_wrapper_argv = (char **) realloc (lto_wrapper_argv, size);
655
      lto_wrapper_argv[lto_wrapper_num_args - 1] = strdup(option);
656
    }
657
}
658
 
659
/* Called by gold after loading the plugin. TV is the transfer vector. */
660
 
661
enum ld_plugin_status
662
onload (struct ld_plugin_tv *tv)
663
{
664
  struct ld_plugin_tv *p;
665
  enum ld_plugin_status status;
666
 
667
  unsigned version = elf_version (EV_CURRENT);
668
  check (version != EV_NONE, LDPL_FATAL, "invalid ELF version");
669
 
670
  p = tv;
671
  while (p->tv_tag)
672
    {
673
      switch (p->tv_tag)
674
        {
675
        case LDPT_MESSAGE:
676
          message = p->tv_u.tv_message;
677
          break;
678
        case LDPT_REGISTER_CLAIM_FILE_HOOK:
679
          register_claim_file = p->tv_u.tv_register_claim_file;
680
          break;
681
        case LDPT_ADD_SYMBOLS:
682
          add_symbols = p->tv_u.tv_add_symbols;
683
          break;
684
        case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
685
          register_all_symbols_read = p->tv_u.tv_register_all_symbols_read;
686
          break;
687
        case LDPT_GET_SYMBOLS:
688
          get_symbols = p->tv_u.tv_get_symbols;
689
          break;
690
        case LDPT_REGISTER_CLEANUP_HOOK:
691
          register_cleanup = p->tv_u.tv_register_cleanup;
692
          break;
693
        case LDPT_ADD_INPUT_FILE:
694
          add_input_file = p->tv_u.tv_add_input_file;
695
          break;
696
        case LDPT_ADD_INPUT_LIBRARY:
697
          add_input_library = p->tv_u.tv_add_input_library;
698
          break;
699
        case LDPT_OPTION:
700
          process_option (p->tv_u.tv_string);
701
          break;
702
        default:
703
          break;
704
        }
705
      p++;
706
    }
707
 
708
  check (register_claim_file, LDPL_FATAL, "register_claim_file not found");
709
  check (add_symbols, LDPL_FATAL, "add_symbols not found");
710
  status = register_claim_file (claim_file_handler);
711
  check (status == LDPS_OK, LDPL_FATAL,
712
         "could not register the claim_file callback");
713
 
714
  if (register_cleanup)
715
    {
716
      status = register_cleanup (cleanup_handler);
717
      check (status == LDPS_OK, LDPL_FATAL,
718
             "could not register the cleanup callback");
719
    }
720
 
721
  if (register_all_symbols_read)
722
    {
723
      check (get_symbols, LDPL_FATAL, "get_symbols not found");
724
      status = register_all_symbols_read (all_symbols_read_handler);
725
      check (status == LDPS_OK, LDPL_FATAL,
726
             "could not register the all_symbols_read callback");
727
    }
728
 
729
  return LDPS_OK;
730
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.