/* Copyright (c) 2024 Freya Murphy */ #ifndef __LINK_H__ #define __LINK_H__ #include #include #include #include #include // when mapping porinters, we need to bounds check to // make sure its in the mapped object file // // this checks that // 1. the end is in the file // 2. the off and len doesnt integer overflow #define BOUND_CHK(obj, len, off) \ (off > UINT32_MAX - len || off + len > obj->mapped_size) // when relocating segments, we need to bounds check to // make sure it wont overflow the addresses past the 32bit // ELF file #define ADDR_CHK(lnk_f, seg_f, max) \ ((lnk_f) > max - (seg_f) || (lnk_f) + (seg_f) > max) // checks if a phdr and shdr are matches #define PHDR_SHDR_MATCH(phdr, shdr) ( \ ((phdr)->p_offset == (shdr)->sh_offset) && \ ((phdr)->p_filesz == (shdr)->sh_size)) \ // start addresses for each tyoe of segment #define TEXT_VADDR_MIN 0x00400000 #define DATA_VADDR_MIN 0x10000000 // alignment of a section #define SEC_ALIGN 0x1000 // pre define struct linker; struct object; struct segment; struct string_table; struct symbol_table; struct symbol_table_mapping; struct glboff_table; /// /// relocation table /// struct relocation_table { uint32_t type; union { void *raw; Elf32_Rel *rel; Elf32_Rela *rela; }; size_t len; struct symbol_table *symtab; }; /// /// string table /// struct string_table { char *data; size_t len; }; int strtab_init(struct string_table *strtab); void strtab_free(struct string_table *strtab); int strtab_push(struct string_table *strtab, const char *str, size_t *res); int strtab_get(struct string_table *strtab, const char *str, size_t *res); /// /// symbol table /// struct symbol_table { struct string_table *strtab; Elf32_Sym *syms; size_t len; size_t size; // mapping for global symbol table // can be null // (metadata for each symbol) struct symbol_table_mapping *map; }; /// /// symbol table map /// (metadata for each symbol) /// int symtab_init(struct symbol_table *symtab); void symtab_free(struct symbol_table *symtab); int symtab_push(struct symbol_table *symtab, const Elf32_Sym *sym); int symtab_get(struct symbol_table *symtab, Elf32_Sym **sym, const char *name, int32_t obj_idx); struct symbol_table_mapping { uint32_t len; uint32_t size; struct object **meta; }; int symtab_map_push(struct symbol_table_mapping *symtabm, struct object *seg); int symtab_map_init(struct symbol_table_mapping *symtabm); void symtab_map_free(struct symbol_table_mapping *symtabm); /// /// segment /// /* a loadable program segment */ struct segment { // segment data char *name; unsigned char *bytes; // current loc uint32_t off; uint32_t vaddr; // new loc uint32_t new_off; uint32_t new_vaddr; // meta bool read; bool write; bool execute; uint32_t align; // size uint32_t size; // phdr Elf32_Phdr *phdr; uint32_t phdr_idx; // shdr Elf32_Shdr *shdr; uint32_t shdr_idx; // object im related to struct object *obj; // segment table entry im related to struct segment_table_entry *ent; // relocation table struct relocation_table reltab; }; int segment_load(struct object *object, struct segment *segment, size_t index); /// /// segment table /// struct segment_table_entry { char *name; uint32_t len; uint32_t size; uint32_t off; uint32_t vaddr; // weak segment pointers. we do not own these!!! struct segment **parts; }; int segtab_ent_init(struct segment_table_entry *ent); void segtab_ent_free(struct segment_table_entry *ent); int segtab_ent_push(struct segment_table_entry *ent, struct segment *seg); uint32_t segtab_ent_size(struct segment_table_entry *ent); // holds each segment by name // and all the segment parts from each of the // object files struct segment_table { uint32_t len; uint32_t size; struct segment_table_entry *entries; }; int segtab_init(struct segment_table *segtab); void segtab_free(struct segment_table *segtab); /* create a new entry with as its first segment part */ int segtab_push(struct segment_table *segtab, struct segment_table_entry **ent, struct segment *seg); /* find a segment table entry with a given name */ int segtab_get(struct segment_table *segtab, struct segment_table_entry **ent, const char *name); /// /// object file /// struct object { // file int fd; char *mapped; size_t mapped_size; // ehdr Elf32_Ehdr *ehdr; // section header table Elf32_Shdr *shdr; size_t shdr_len; // program table Elf32_Phdr *phdr; size_t phdr_len; // phdr <=> shdr mappings uint32_t *phdr_to_shdr_mapping; // object meta const char *name; size_t index; // segments size_t segment_len; struct segment *segments; // section header strtab struct string_table *shstrtab; // strtabs struct string_table *strtabs; // symtabs struct symbol_table *symtabs; }; int object_load(struct object *object, char *path, uint32_t index); void object_free(struct object *object); /// /// linker /// struct linker { size_t obj_len; struct object *objects; struct linker_arguments *args; // current pointers to relocate // sections uint32_t off; uint32_t text_vaddr; uint32_t data_vaddr; // elf tables struct string_table shstrtab; struct string_table strtab; struct symbol_table symtab; struct symbol_table_mapping symtab_map; // output elf Elf32_Ehdr ehdr; Elf32_Phdr *phdr; uint32_t phdr_len; Elf32_Shdr *shdr; uint32_t shdr_len; uint32_t symtab_shidx; uint32_t strtab_shidx; uint32_t shstrtab_shidx; // all segments struct segment_table segments; }; /* defines arguments to the linker */ struct linker_arguments { char **in_files; int in_count; char *out_file; bool freestanding; }; /* link object files */ int link_files(struct linker_arguments args); #endif /* __LINK_H__ */