| /* SPDX-License-Identifier: BSD-2-Clause */ |
| /* Copyright 1996-2020 The NASM Authors - All Rights Reserved */ |
| |
| /* |
| * outlib.c |
| * |
| * Common routines for the output backends. |
| */ |
| |
| #include "outlib.h" |
| #include "raa.h" |
| |
| uint64_t realsize(enum out_type type, uint64_t size) |
| { |
| switch (type) { |
| case OUT_REL1ADR: |
| return 1; |
| case OUT_REL2ADR: |
| return 2; |
| case OUT_REL4ADR: |
| return 4; |
| case OUT_REL8ADR: |
| return 8; |
| default: |
| return size; |
| } |
| } |
| |
| /* Common section/symbol handling */ |
| |
| struct ol_sect *_ol_sect_list; |
| uint64_t _ol_nsects; /* True sections, not external symbols */ |
| static struct ol_sect **ol_sect_tail = &_ol_sect_list; |
| static struct hash_table ol_secthash; |
| static struct RAA *ol_sect_index_tbl; |
| |
| struct ol_sym *_ol_sym_list; |
| uint64_t _ol_nsyms; |
| static struct ol_sym **ol_sym_tail = &_ol_sym_list; |
| static struct hash_table ol_symhash; |
| |
| void ol_init(void) |
| { |
| } |
| |
| static void ol_free_symbols(void) |
| { |
| struct ol_sym *s, *stmp; |
| |
| hash_free(&ol_symhash); |
| |
| list_for_each_safe(s, stmp, _ol_sym_list) { |
| nasm_free((char *)s->name); |
| nasm_free(s); |
| } |
| |
| _ol_nsyms = 0; |
| _ol_sym_list = NULL; |
| ol_sym_tail = &_ol_sym_list; |
| } |
| |
| static void ol_free_sections(void) |
| { |
| struct ol_sect *s, *stmp; |
| |
| hash_free(&ol_secthash); |
| raa_free(ol_sect_index_tbl); |
| ol_sect_index_tbl = NULL; |
| |
| list_for_each_safe(s, stmp, _ol_sect_list) { |
| saa_free(s->data); |
| saa_free(s->reloc); |
| nasm_free((char *)s->name); |
| nasm_free(s); |
| } |
| |
| _ol_nsects = 0; |
| _ol_sect_list = NULL; |
| ol_sect_tail = &_ol_sect_list; |
| } |
| |
| void ol_cleanup(void) |
| { |
| ol_free_symbols(); |
| ol_free_sections(); |
| } |
| |
| /* |
| * Allocate a section index and add a section, subsection, or external |
| * symbol to the section-by-index table. If the index provided is zero, |
| * allocate a new index via seg_alloc(). |
| */ |
| static uint32_t ol_seg_alloc(void *s, uint32_t ix) |
| { |
| if (!ix) |
| ix = seg_alloc(); |
| ol_sect_index_tbl = raa_write_ptr(ol_sect_index_tbl, ix >> 1, s); |
| return ix; |
| } |
| |
| /* |
| * Find a section or create a new section structure if it does not exist |
| * and allocate it an index value via seg_alloc(). |
| */ |
| struct ol_sect *_ol_get_sect(const char *name, size_t ssize, size_t rsize) |
| { |
| struct ol_sect *s, **sp; |
| struct hash_insert hi; |
| |
| sp = (struct ol_sect **)hash_find(&ol_secthash, name, &hi); |
| if (sp) |
| return *sp; |
| |
| s = nasm_zalloc(ssize); |
| s->syml.tail = &s->syml.head; |
| s->name = nasm_strdup(name); |
| s->data = saa_init(1); |
| s->reloc = saa_init(rsize); |
| *ol_sect_tail = s; |
| ol_sect_tail = &s->next; |
| _ol_nsects++; |
| s->index = s->subindex = ol_seg_alloc(s, 0); |
| |
| hash_add(&hi, s->name, s); |
| return s; |
| } |
| |
| /* Find a section by name without creating one */ |
| struct ol_sect *_ol_sect_by_name(const char *name) |
| { |
| struct ol_sect **sp; |
| |
| sp = (struct ol_sect **)hash_find(&ol_secthash, name, NULL); |
| return sp ? *sp : NULL; |
| } |
| |
| /* Find a section or external symbol by index; NULL if not valid */ |
| struct ol_sect *_ol_sect_by_index(int32_t index) |
| { |
| uint32_t ix = index; |
| |
| if (unlikely(ix >= SEG_ABS)) |
| return NULL; |
| |
| return raa_read_ptr(ol_sect_index_tbl, ix >> 1); |
| } |
| |
| /* |
| * Start a new subsection for the given section. At the moment, once a |
| * subsection has been created, it is not possible to revert to an |
| * earlier subsection. ol_sect_by_index() will return the main section |
| * structure. Returns the new section index. This is used to prevent |
| * the front end from optimizing across subsection boundaries. |
| */ |
| int32_t _ol_new_subsection(struct ol_sect *sect) |
| { |
| if (unlikely(!sect)) |
| return NO_SEG; |
| |
| return sect->subindex = ol_seg_alloc(sect, 0); |
| } |
| |
| /* |
| * Insert a symbol into a list; need to use upcasting using container_of() |
| * to walk the list later. |
| */ |
| static void ol_add_sym_to(struct ol_symlist *syml, struct ol_symhead *head, |
| uint64_t offset) |
| { |
| syml->tree.key = offset; |
| head->tree = rb_insert(head->tree, &syml->tree); |
| *head->tail = syml; |
| head->tail = &syml->next; |
| head->n++; |
| } |
| |
| /* |
| * Create a location structure from seg:offs |
| */ |
| void ol_mkloc(struct ol_loc *loc, int64_t offs, int32_t seg) |
| { |
| nasm_zero(*loc); |
| loc->offs = offs; |
| |
| if (unlikely((uint32_t)seg >= SEG_ABS)) { |
| if (likely(seg == NO_SEG)) { |
| loc->seg.t = OS_NOSEG; |
| } else { |
| loc->seg.t = OS_ABS; |
| loc->seg.index = seg - SEG_ABS; |
| } |
| } else { |
| loc->seg.index = seg & ~1; |
| loc->seg.t = OS_SECT | (seg & 1); |
| loc->seg.s.sect = _ol_sect_by_index(loc->seg.index); |
| } |
| } |
| |
| /* |
| * Create a new symbol. If this symbol is OS_OFFS, add it to the relevant |
| * section, too. If the symbol already exists, return NULL; this is |
| * different from ol_get_section() as a single section may be invoked |
| * many times. On the contrary, the front end will prevent a single symbol |
| * from being defined more than once. |
| * |
| * If flags has OF_GLOBAL set, add it to the global symbol hash for |
| * the containing section if applicable. |
| * |
| * If flags has OF_IMPSEC set, allocate a segment index for it via |
| * seg_alloc() unless v->index is already set, and add it to the |
| * section by index list. |
| */ |
| struct ol_sym *_ol_new_sym(const char *name, const struct ol_loc *v, |
| uint32_t flags, size_t size) |
| { |
| struct hash_insert hi; |
| struct ol_sym *sym; |
| |
| if (hash_find(&ol_symhash, name, &hi)) |
| return NULL; /* Symbol already exists */ |
| |
| flags |= OF_SYMBOL; |
| |
| sym = nasm_zalloc(size); |
| sym->name = nasm_strdup(name); |
| sym->v = *v; |
| |
| if (sym->v.seg.t & OS_SECT) { |
| struct ol_sect *sect = sym->v.seg.s.sect; |
| |
| if (!sect || (sect->flags & OF_SYMBOL)) |
| /* Must be an external or common reference */ |
| flags |= OF_IMPSEC; |
| |
| if (flags & OF_IMPSEC) { |
| /* Metasection */ |
| if (!sym->v.seg.s.sym) { |
| sym->v.seg.s.sym = sym; |
| sym->v.seg.index = ol_seg_alloc(sym, sym->v.seg.index); |
| } |
| } else if (sym->v.seg.t == OS_OFFS) { |
| struct ol_sect * const sect = sym->v.seg.s.sect; |
| const uint64_t offs = sym->v.offs; |
| |
| ol_add_sym_to(&sym->syml, §->syml, offs); |
| if (flags & OF_GLOBAL) |
| ol_add_sym_to(&sym->symg, §->symg, offs); |
| } |
| } |
| sym->flags = flags; |
| |
| *ol_sym_tail = sym; |
| ol_sym_tail = &sym->next; |
| _ol_nsyms++; |
| |
| hash_add(&hi, sym->name, sym); |
| return sym; |
| } |
| |
| /* Find a symbol in the global namespace */ |
| struct ol_sym *_ol_sym_by_name(const char *name) |
| { |
| struct ol_sym **symp; |
| |
| symp = (struct ol_sym **)hash_find(&ol_symhash, name, NULL); |
| return symp ? *symp : NULL; |
| } |
| |
| /* |
| * Find a symbol by address in a specific section. If no symbol is defined |
| * at that exact address, return the immediately previously defined one. |
| * If global is set, then only return global symbols. |
| */ |
| struct ol_sym *_ol_sym_by_address(struct ol_sect *sect, int64_t addr, |
| bool global) |
| { |
| struct ol_symhead *head; |
| size_t t_offs; |
| struct rbtree *t; |
| |
| if (global) { |
| head = §->symg; |
| t_offs = offsetof(struct ol_sym, symg.tree); |
| } else { |
| head = §->syml; |
| t_offs = offsetof(struct ol_sym, syml.tree); |
| } |
| |
| t = rb_search(head->tree, addr); |
| if (!t) |
| return NULL; |
| |
| return (struct ol_sym *)((char *)t - t_offs); |
| } |