1 /* pk-map.c - Support for map files. */
2
3 /* Copyright (C) 2020, 2021 Jose E. Marchesi */
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 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU 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. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <inttypes.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <xalloc.h>
26 #include <assert.h>
27 #include "basename-lgpl.h"
28
29 #include "poke.h"
30 #include "pk-utils.h"
31 #include "pk-term.h"
32 #include "pk-map.h"
33 #include "pk-map-parser.h"
34
35 /* Unique map ID.
36 This number is unique per map in a poke session. */
37
38 static uint64_t next_map_id;
39
40 /* Maps for a given IOS.
41
42 IOS_ID is the identifier of the ios.
43 MAPS is a list of chained maps.
44 CHAIN is a pointer to another next map_ios, or NULL. */
45
46 #define PK_MAP_IOS_ID(MAP_IOS) ((MAP_IOS)->ios_id)
47 #define PK_MAP_IOS_MAPS(MAP_IOS) ((MAP_IOS)->maps)
48 #define PK_MAP_IOS_CHAIN(MAP_IOS) ((MAP_IOS)->chain)
49
50 struct pk_map_ios
51 {
52 int ios_id;
53 struct pk_map *maps;
54 struct pk_map_ios *chain;
55 };
56
57 typedef struct pk_map_ios *pk_map_ios;
58
59 /* Global containing the IOS maps defined in the running poke. */
60 static struct pk_map_ios *poke_maps;
61
62 static pk_map_ios
search_map_ios(int ios_id)63 search_map_ios (int ios_id)
64 {
65 pk_map_ios map_ios;
66
67 for (map_ios = poke_maps;
68 map_ios;
69 map_ios = PK_MAP_IOS_CHAIN (map_ios))
70 {
71 if (PK_MAP_IOS_ID (map_ios) == ios_id)
72 break;
73 }
74
75 return map_ios;
76 }
77
78 static void
free_entry(pk_map_entry entry)79 free_entry (pk_map_entry entry)
80 {
81 free (PK_MAP_ENTRY_VARNAME (entry));
82 free (entry);
83 }
84
85 static void
free_map(pk_map map)86 free_map (pk_map map)
87 {
88 pk_map_entry entry, tmp;
89
90 for (entry = PK_MAP_ENTRIES (map); entry; entry = tmp)
91 {
92 tmp = PK_MAP_CHAIN (entry);
93 free_entry (entry);
94 }
95
96 free (PK_MAP_NAME (map));
97 free (PK_MAP_SOURCE (map));
98 free (map);
99 }
100
101 static pk_map
search_map(pk_map_ios map_ios,const char * mapname)102 search_map (pk_map_ios map_ios, const char *mapname)
103 {
104 pk_map map;
105
106 for (map = PK_MAP_IOS_MAPS (map_ios);
107 map;
108 map = PK_MAP_CHAIN (map))
109 {
110 if (STREQ (PK_MAP_NAME (map), mapname))
111 break;
112 }
113
114 return map;
115 }
116
117 static pk_map_entry
search_map_entry(pk_map map,const char * name)118 search_map_entry (pk_map map, const char *name)
119 {
120 pk_map_entry map_entry;
121
122 for (map_entry = PK_MAP_ENTRIES (map);
123 map_entry;
124 map_entry = PK_MAP_ENTRY_CHAIN (map_entry))
125 {
126 if (STREQ (PK_MAP_ENTRY_NAME (map_entry), name))
127 break;
128 }
129
130 return map_entry;
131 }
132
133 static char *
entry_name_to_varname(const char * name)134 entry_name_to_varname (const char *name)
135 {
136 char *varname;
137
138 if (asprintf (&varname, "__map_entry_%" PRIu64 "_%s", next_map_id, name) == -1)
139 pk_fatal (_("out of memory"));
140 return varname;
141 }
142
143 static char *
pk_map_alien_token_handler(const char * id,char ** errmsg)144 pk_map_alien_token_handler (const char *id, char **errmsg)
145 {
146 char *map_name = NULL;
147 char *entry_name = NULL;
148 pk_ios cur_ios;
149
150 *errmsg = NULL;
151
152 /* No point on going ahead if there is no current IOS. */
153 cur_ios = pk_ios_cur (poke_compiler);
154 if (!cur_ios)
155 goto error;
156
157 /* The format of the identifier should be:
158
159 $MAPNAME::ENTRYNAME
160
161 i.e. the identifier should have two components. Verify this
162 holds for ID, and extract the fields. */
163
164 entry_name = strstr (id, "::");
165 if (!entry_name || strstr (entry_name + 2, "::"))
166 goto error;
167
168 if (entry_name)
169 {
170 pk_map map;
171 int ios_id;
172
173 map_name = xmalloc (entry_name - id + 1);
174 strncpy (map_name, id, entry_name - id);
175 map_name[entry_name - id] = '\0';
176 entry_name += 2;
177 ios_id = pk_ios_get_id (cur_ios);
178
179 map = pk_map_search (ios_id, map_name);
180 if (map)
181 {
182 pk_map_entry entry;
183
184 for (entry = PK_MAP_ENTRIES (map);
185 entry;
186 entry = PK_MAP_ENTRY_CHAIN (entry))
187 {
188 if (STREQ (PK_MAP_ENTRY_NAME (entry), entry_name))
189 return xstrdup (PK_MAP_ENTRY_VARNAME (entry));
190 }
191 }
192
193 free (map_name);
194
195 }
196
197 error:
198 *errmsg = xstrdup ("invalid map entry");
199 return NULL;
200 }
201
202 void
pk_map_init(void)203 pk_map_init (void)
204 {
205 poke_maps = NULL;
206
207 /* Install the handler for alien variables that recognizes map
208 entries. */
209 pk_set_alien_token_fn (poke_compiler, pk_map_alien_token_handler);
210 }
211
212 void
pk_map_shutdown(void)213 pk_map_shutdown (void)
214 {
215 struct pk_map_ios *map_ios, *next_map_ios;
216
217 for (map_ios = poke_maps; map_ios; map_ios = next_map_ios)
218 {
219 struct pk_map *map, *next_map;
220
221 for (map = PK_MAP_IOS_MAPS (map_ios); map; map = next_map)
222 {
223 struct pk_map_entry *entry, *next_entry;
224
225 for (entry = PK_MAP_ENTRIES (map);
226 entry;
227 entry = next_entry)
228 {
229 next_entry = PK_MAP_ENTRY_CHAIN (entry);
230 free (entry->varname);
231 free (entry);
232 }
233
234 next_map = PK_MAP_CHAIN (map);
235 free (map->name);
236 free (map);
237 }
238
239 next_map_ios = PK_MAP_IOS_CHAIN (map_ios);
240 free (map_ios);
241 }
242
243 poke_maps = NULL;
244 }
245
246 int
pk_map_create(int ios_id,const char * mapname,const char * source)247 pk_map_create (int ios_id, const char *mapname,
248 const char *source)
249 {
250 pk_map_ios map_ios;
251
252 /* Search for the right map_ios in poke_maps. */
253 map_ios = search_map_ios (ios_id);
254
255 /* If there is not a map_ios entry for this IO space, create
256 one. */
257 if (!map_ios)
258 {
259 map_ios = xmalloc (sizeof (struct pk_map_ios));
260 PK_MAP_IOS_ID (map_ios) = ios_id;
261 PK_MAP_IOS_MAPS (map_ios) = NULL;
262
263 PK_MAP_IOS_CHAIN (map_ios) = poke_maps;
264 poke_maps = map_ios;
265 }
266
267 /* Create a new map and add it to the chain of maps in the
268 map_ios. */
269 {
270 pk_map maps, map;
271
272 maps = pk_map_get_maps (ios_id);
273
274 /* Make sure there is not already a map with the given name. */
275 for (map = maps; map; map = PK_MAP_CHAIN (map))
276 {
277 if (STREQ (PK_MAP_NAME (map), mapname))
278 return 0;
279 }
280
281 /* Create an empty map and add it to the sequence. */
282 map = xmalloc (sizeof (struct pk_map));
283 PK_MAP_ID (map) = next_map_id++;
284 PK_MAP_NAME (map) = xstrdup (mapname);
285 if (source)
286 PK_MAP_SOURCE (map) = xstrdup (source);
287 else
288 PK_MAP_SOURCE (map) = NULL;
289 PK_MAP_ENTRIES (map) = NULL;
290
291 PK_MAP_CHAIN (map) = PK_MAP_IOS_MAPS (map_ios);
292 PK_MAP_IOS_MAPS (map_ios) = map;
293 }
294
295 return 1;
296 }
297
298 int
pk_map_remove(int ios_id,const char * mapname)299 pk_map_remove (int ios_id, const char *mapname)
300 {
301 pk_map_ios map_ios;
302 pk_map prev, map;
303
304 /* Search for the right map_ios in poke_maps. */
305 map_ios = search_map_ios (ios_id);
306
307 if (!map_ios)
308 return 0;
309
310 for (prev = NULL, map = PK_MAP_IOS_MAPS (map_ios);
311 map;
312 prev = map, map = PK_MAP_CHAIN (map))
313 {
314 if (STREQ (PK_MAP_NAME (map), mapname))
315 break;
316 }
317
318 if (map == NULL)
319 /* Map not found. */
320 return 0;
321
322 if (prev)
323 PK_MAP_CHAIN (prev) = PK_MAP_CHAIN (map);
324 else
325 PK_MAP_IOS_MAPS (map_ios) = PK_MAP_CHAIN (map);
326
327 free_map (map);
328
329 return 1;
330 }
331
332 pk_map
pk_map_search(int ios_id,const char * name)333 pk_map_search (int ios_id, const char *name)
334 {
335 pk_map maps = pk_map_get_maps (ios_id);
336
337 if (maps)
338 {
339 pk_map map;
340
341 for (map = maps; map; map = PK_MAP_CHAIN (map))
342 if (STREQ (PK_MAP_NAME (map), name))
343 return map;
344 }
345
346 return NULL;
347 }
348
349 int
pk_map_add_entry(int ios_id,const char * mapname,const char * name,const char * varname,pk_val offset)350 pk_map_add_entry (int ios_id, const char *mapname,
351 const char *name, const char *varname,
352 pk_val offset)
353 {
354 pk_map_ios map_ios;
355 pk_map map;
356 pk_map_entry entry;
357
358 map_ios = search_map_ios (ios_id);
359 if (!map_ios)
360 return 0;
361
362 map = search_map (map_ios, mapname);
363 if (!map)
364 return 0;
365
366 entry = search_map_entry (map, name);
367 if (entry)
368 return 0;
369
370 /* Create a new entry and chain it in the map. The entries are kept
371 sorted by offset. */
372 entry = xmalloc (sizeof (struct pk_map_entry));
373 PK_MAP_ENTRY_NAME (entry) = xstrdup (name);
374 PK_MAP_ENTRY_VARNAME (entry) = xstrdup (varname);
375 PK_MAP_ENTRY_OFFSET (entry) = offset;
376
377 if (PK_MAP_ENTRIES (map) == NULL)
378 {
379 PK_MAP_ENTRY_CHAIN (entry) = NULL;
380 PK_MAP_ENTRIES (map) = entry;
381 }
382 else
383 {
384 pk_map_entry e, p;
385 uint64_t offset_bits
386 = (pk_uint_value (pk_offset_magnitude (offset))
387 * pk_uint_value (pk_offset_unit (offset)));
388
389 for (p = NULL, e = PK_MAP_ENTRIES (map);
390 e;
391 p = e, e = PK_MAP_ENTRY_CHAIN (e))
392 {
393 pk_val e_offset = PK_MAP_ENTRY_OFFSET (e);
394
395 if ((pk_uint_value (pk_offset_magnitude (e_offset))
396 * pk_uint_value (pk_offset_unit (e_offset)))
397 > offset_bits)
398 break;
399 }
400
401
402 PK_MAP_ENTRY_CHAIN (entry) = e;
403 if (p)
404 PK_MAP_ENTRY_CHAIN (p) = entry;
405 else
406 PK_MAP_ENTRIES (map) = entry;
407 }
408
409 return 1;
410 }
411
412 int
pk_map_remove_entry(int ios_id,const char * mapname,const char * entryname)413 pk_map_remove_entry (int ios_id, const char *mapname,
414 const char *entryname)
415 {
416 pk_map_ios map_ios;
417 pk_map map;
418 pk_map_entry entry, prev;
419
420 map_ios = search_map_ios (ios_id);
421 if (!map_ios)
422 return 0;
423
424 map = search_map (map_ios, mapname);
425 if (!map)
426 return 0;
427
428 for (prev = NULL, entry = PK_MAP_ENTRIES (map);
429 entry;
430 prev = entry, entry = PK_MAP_ENTRY_CHAIN (entry))
431 {
432 if (STREQ (PK_MAP_ENTRY_NAME (entry), entryname))
433 {
434 if (prev)
435 PK_MAP_ENTRY_CHAIN (prev) = PK_MAP_ENTRY_CHAIN (entry);
436 else
437 PK_MAP_ENTRIES (map) = PK_MAP_ENTRY_CHAIN (entry);
438
439 free_entry (entry);
440 return 1;
441 }
442 }
443
444 return 0;
445 }
446
447 pk_map
pk_map_get_maps(int ios_id)448 pk_map_get_maps (int ios_id)
449 {
450 pk_map_ios map_ios;
451
452 for (map_ios = poke_maps;
453 map_ios;
454 map_ios = PK_MAP_IOS_CHAIN (map_ios))
455 {
456 if (PK_MAP_IOS_ID (map_ios) == ios_id)
457 return PK_MAP_IOS_MAPS (map_ios);
458 }
459
460 return NULL;
461 }
462
463 static int
pk_map_load_parsed_map(int ios_id,const char * mapname,const char * filename,pk_map_parsed_map map)464 pk_map_load_parsed_map (int ios_id, const char *mapname,
465 const char *filename,
466 pk_map_parsed_map map)
467 {
468 pk_map_parsed_entry entry;
469
470 /* First, compile the prologue. */
471 /* XXX set error location and disable verbose error messages in
472 poke_compiler. */
473 if (pk_compile_buffer (poke_compiler,
474 PK_MAP_PARSED_MAP_PROLOGUE (map),
475 NULL) != PK_OK)
476 return 0;
477
478 /* Process the map entries and create the mapped global
479 variables. */
480
481 for (entry = PK_MAP_PARSED_MAP_ENTRIES (map);
482 entry;
483 entry = PK_MAP_PARSED_ENTRY_CHAIN (entry))
484 {
485 pk_val val;
486 int process_p = 1;
487 const char *condition = PK_MAP_PARSED_ENTRY_CONDITION (entry);
488
489 /* Evaluate the condition. */
490 if (condition)
491 {
492 /* XXX set error location... */
493 if (pk_compile_expression (poke_compiler,
494 condition,
495 NULL /* end */,
496 &val) != PK_OK)
497 goto error;
498
499 if (pk_type_code (pk_typeof (val)) != PK_INT
500 && pk_type_code (pk_typeof (val)) != PK_UINT)
501 {
502 /* XXX error location. */
503 pk_printf ("error: invalid condition expression\n");
504 goto error;
505 }
506
507 if (!(pk_type_code (pk_typeof (val)) == PK_INT
508 ? pk_int_value (val) : pk_uint_value (val)))
509 process_p = 0;
510 }
511
512 PK_MAP_PARSED_ENTRY_SKIPPED_P (entry) = !process_p;
513 if (process_p)
514 {
515 const char *name = PK_MAP_PARSED_ENTRY_NAME (entry);
516 const char *type = PK_MAP_PARSED_ENTRY_TYPE (entry);
517 const char *offset = PK_MAP_PARSED_ENTRY_OFFSET (entry);
518
519 PK_MAP_PARSED_ENTRY_VARNAME (entry)
520 = entry_name_to_varname (name);
521
522 /* XXX set error location with compiler pragmas... */
523 char *defvar_str
524 = pk_str_concat ("var ",
525 PK_MAP_PARSED_ENTRY_VARNAME (entry),
526 " = ", type, " @ ", offset, ";", NULL);
527
528 /* XXX what about constraints? */
529 if (pk_compile_buffer (poke_compiler,
530 defvar_str,
531 NULL /* end */) != PK_OK)
532 goto error;
533 }
534 }
535
536 /* Create the map. */
537 if (!pk_map_create (ios_id, mapname, filename))
538 return 0;
539
540 /* Add the map entries. */
541 for (entry = PK_MAP_PARSED_MAP_ENTRIES (map);
542 entry;
543 entry = PK_MAP_PARSED_ENTRY_CHAIN (entry))
544 {
545 if (!PK_MAP_PARSED_ENTRY_SKIPPED_P (entry))
546 {
547 char *name = PK_MAP_PARSED_ENTRY_NAME (entry);
548 char *varname = PK_MAP_PARSED_ENTRY_VARNAME (entry);
549 pk_val offset;
550
551 offset = pk_decl_val (poke_compiler, varname);
552 assert (offset != PK_NULL);
553 offset = pk_val_offset (offset);
554
555 if (!pk_map_add_entry (ios_id, mapname, name,
556 varname, offset))
557 goto error;
558 }
559 }
560
561 return 1;
562
563 error:
564 return 0;
565 }
566
567 char *
pk_map_normalize_name(const char * str)568 pk_map_normalize_name (const char *str)
569 {
570 char *mapname, *p;
571
572 mapname = xstrdup (str);
573
574 /* Strip the ".map" extension. */
575 if (strlen (mapname) > 4
576 && STREQ (mapname + strlen (mapname) - 4, ".map"))
577 mapname[strlen (mapname) - 4] = '\0';
578
579 /* Normalize the name, which basically consists on turning any
580 character not in [0-9a-zA-Z_] into _ */
581 for (p = mapname; *p != '\0'; ++p)
582 {
583 if (!((*p >= '0' && *p <= '9')
584 || (*p >= 'a' && *p <= 'z')
585 || (*p >= 'A' && *p <= 'Z')
586 || (*p == '_')))
587 *p = '_';
588 }
589
590 return mapname;
591 }
592
593 int
pk_map_load_file(int ios_id,const char * path,char ** errmsg)594 pk_map_load_file (int ios_id,
595 const char *path, char **errmsg)
596 {
597 char *emsg, *mapname;
598 FILE *fp;
599 pk_map_parsed_map parsed_map;
600
601 /* Do not attempt to load the mapfile if there is already a map with
602 the same name defined in the IO space. */
603 mapname = pk_map_normalize_name (last_component (path));
604 if (pk_map_search (ios_id, mapname) != NULL)
605 {
606 *errmsg = "map already loaded";
607 return 0;
608 }
609
610 /* Open the file whose contents are to be parsed. */
611 if ((emsg = pk_file_readable (path)) != NULL)
612 {
613 *errmsg = emsg;
614 return 0;
615 }
616
617 fp = fopen (path, "r");
618 if (!fp)
619 {
620 *errmsg = strerror (errno);
621 free (mapname);
622 return 0;
623 }
624
625 /* Parse the file contents and close the file. */
626 parsed_map = pk_map_parse_file (path, fp);
627 if (!parsed_map)
628 {
629 if (errmsg)
630 *errmsg = "";
631 return 0;
632 }
633
634 if (fclose (fp) == EOF)
635 {
636 *errmsg = strerror (errno);
637 free (mapname);
638 return 0;
639 }
640
641 /* XXX */
642 // pk_map_print_parsed_map (parsed_map);
643
644 /* Process the result. */
645 if (!pk_map_load_parsed_map (ios_id,
646 mapname,
647 path,
648 parsed_map))
649 {
650 free (mapname);
651 return 0;
652 }
653
654 free (mapname);
655 return 1;
656 }
657
658 char *
pk_map_resolve_map(const char * mapname,int filename_p)659 pk_map_resolve_map (const char *mapname, int filename_p)
660 {
661 pk_val val;
662 const char *map_load_path;
663 char *full_filename = NULL;
664
665 val = pk_decl_val (poke_compiler, "map_load_path");
666 if (val == PK_NULL)
667 pk_fatal ("couldn't get `map_load_path'");
668
669 if (pk_type_code (pk_typeof (val)) != PK_STRING)
670 pk_fatal ("map_load_path should be a string");
671
672 map_load_path = pk_string_str (val);
673
674 /* Traverse the directories in the load path and try to load the
675 requested map. */
676 {
677 const char *ext = filename_p ? "" : ".map";
678 const char *s, *e;
679
680 char *fixed_load_path
681 = pk_str_replace (map_load_path, "%DATADIR%", PKGDATADIR);
682
683 for (s = fixed_load_path, e = s; *e; s = e + 1)
684 {
685 /* Ignore empty entries. */
686 if ((e = strchrnul (s, ':')) == s)
687 continue;
688
689 asprintf (&full_filename, "%.*s/%s%s", (int) (e - s), s, mapname, ext);
690
691 if (pk_file_readable (full_filename) == NULL)
692 break;
693
694 free (full_filename);
695 full_filename = NULL;
696 }
697
698 if (fixed_load_path != map_load_path)
699 free (fixed_load_path);
700 }
701
702 return full_filename;
703 }
704