/* Return the path contained in the .gnu_debuglink section or NULL if we cannot * find it. * * XXX: There's also a CRC in here that we could use to warn if the files are * out of sync. */ const char * drsym_obj_debuglink_section(void *mod_in) { elf_info_t *mod = (elf_info_t *) mod_in; Elf_Shdr *section_header = elf_getshdr(find_elf_section_by_name(mod->elf, ".gnu_debuglink")); if (section_header == NULL) { NOTIFY_ELF(); return NULL; } return ((char*) mod->map_base) + section_header->sh_offset; }
/* returns the *internal* offset of __start. (that's what the symbol table contains in any case) The absolute address is different, offset by the .vma field of the .text section (where __start resides) returns -1 on error */ long get_start_addr (Elf * elf) { const char STARTSYM[] = "__start"; Elf_Scn *temp_scn = NULL; Elf32_Shdr * shdr; size_t shstrndx; /* not too sure what this means */ if (elf_getshstrndx(elf, &shstrndx) == 0) { fprintf (stderr, "getshstrndx() failed: %s\n", elf_errmsg(-1)); goto error_egress; } /* iterate over the headers. */ while ( (temp_scn = elf_nextscn(elf, temp_scn)) != NULL) { shdr = elf_getshdr (temp_scn); if (shdr == NULL) { fprintf (stderr, "elf_getshdr() failed: %s\n", elf_errmsg(-1)); goto error_egress; } if ( (name = elf_strptr(elf, shstrndx, shdr.sh_name) ) == NULL) { fprintf (stderr, "elf_strptr() failed: %s\n", elf_errmsg(-1)); goto error_egress; } if (strncmp (STARTSYM, name, sizeof(STARTSYM)) != 0) { /* wrong name */ continue; } /* We got it! */ } error_egress: }
void * drsym_obj_mod_init_pre(byte *map_base, size_t file_size) { elf_info_t *mod; Elf_Scn *symtab_scn; Elf_Scn *strtab_scn; Elf_Shdr *symtab_shdr; mod = dr_global_alloc(sizeof(*mod)); memset(mod, 0, sizeof(*mod)); mod->map_base = map_base; mod->elf = elf_memory((char *)map_base, file_size); symtab_scn = find_elf_section_by_name(mod->elf, ".symtab"); strtab_scn = find_elf_section_by_name(mod->elf, ".strtab"); if (symtab_scn != NULL) { mod->debug_kind |= DRSYM_SYMBOLS | DRSYM_ELF_SYMTAB; if (strtab_scn != NULL) { symtab_shdr = elf_getshdr(symtab_scn); mod->strtab_idx = elf_ndxscn(strtab_scn); mod->num_syms = symtab_shdr->sh_size / symtab_shdr->sh_entsize; /* This assumes that the ELF file uses the same representation conventions * as the current machine, which is reasonable considering this module is * probably loaded in the current process. */ mod->syms = (Elf_Sym*)(((char*) mod->map_base) + symtab_shdr->sh_offset); } } else { /* XXX i#672: there may still be dwarf2 or stabs sections even if the * symtable is stripped and we could do symbol lookup via dwarf2 */ } if (find_elf_section_by_name(mod->elf, ".debug_line") != NULL) { mod->debug_kind |= DRSYM_LINE_NUMS | DRSYM_DWARF_LINE; } return (void *) mod; }
/* Looks for a section with real data, not just a section with a header */ static Elf_Scn * find_elf_section_by_name(Elf *elf, const char *match_name) { Elf_Scn *scn; size_t shstrndx; /* Means "section header string table section index" */ if (elf_getshdrstrndx(elf, &shstrndx) != 0) { NOTIFY_ELF(); return NULL; } for (scn = elf_getscn(elf, 0); scn != NULL; scn = elf_nextscn(elf, scn)) { Elf_Shdr *section_header = elf_getshdr(scn); const char *sec_name; if (section_header == NULL) { NOTIFY_ELF(); continue; } sec_name = elf_strptr(elf, shstrndx, section_header->sh_name); if (sec_name == NULL) { NOTIFY_ELF(); } if (strcmp(sec_name, match_name) == 0) { /* For our purposes, we want to treat a no-data section * type as if it didn't exist. This happens sometimes in * debuglink files where some sections like .symtab are * present b/c the headers mirror the original ELF file, but * there's no data there. Xref i#642. */ if (TEST(SHT_NOBITS, section_header->sh_type)) return NULL; return scn; } } return NULL; }
/* * Archive members are typically extracted to resolve an existing undefined * reference. However, other symbol definitions can cause archive members to * be processed to determine if the archive member provides a more appropriate * definition. This routine processes the archive member to determine if the * member is really required. * * i. Tentative symbols may cause the extraction of an archive member. * If the archive member has a strong defined symbol it will be used. * If the archive member simply contains another tentative definition, * or a defined function symbol, then it will not be used. * * ii. A symbol reference may define a hidden or protected visibility. The * reference can only be bound to a definition within a relocatable object * for this restricted visibility to be satisfied. If the archive member * provides a definition of the same symbol type, this definition is * taken. The visibility of the defined symbol is irrelevant, as the most * restrictive visibility of the reference and the definition will be * applied to the final symbol. * * exit: * Returns 1 if there is a match, 0 if no match is seen, and S_ERROR if an * error occurred. */ static uintptr_t process_member(Ar_mem *amp, const char *name, Sym_desc *sdp, Ofl_desc *ofl) { Sym *syms, *osym = sdp->sd_sym; Xword symn, cnt; char *strs; /* * Find the first symbol table in the archive member, obtain its * data buffer and determine the number of global symbols (Note, * there must be a symbol table present otherwise the archive would * never have been able to generate its own symbol entry for this * member). */ if (amp->am_syms == NULL) { Elf_Scn *scn = NULL; Shdr *shdr; Elf_Data *data; while (scn = elf_nextscn(amp->am_elf, scn)) { if ((shdr = elf_getshdr(scn)) == NULL) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETSHDR), amp->am_path); return (S_ERROR); } if ((shdr->sh_type == SHT_SYMTAB) || (shdr->sh_type == SHT_DYNSYM)) break; } if ((data = elf_getdata(scn, NULL)) == NULL) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETDATA), amp->am_path); return (S_ERROR); } syms = (Sym *)data->d_buf; syms += shdr->sh_info; symn = shdr->sh_size / shdr->sh_entsize; symn -= shdr->sh_info; /* * Get the data for the associated string table. */ if ((scn = elf_getscn(amp->am_elf, (size_t)shdr->sh_link)) == NULL) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETSCN), amp->am_path); return (S_ERROR); } if ((data = elf_getdata(scn, NULL)) == NULL) { ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETDATA), amp->am_path); return (S_ERROR); } strs = data->d_buf; /* * Initialize the archive member structure in case we have to * come through here again. */ amp->am_syms = syms; amp->am_strs = strs; amp->am_symn = symn; } else { syms = amp->am_syms; strs = amp->am_strs; symn = amp->am_symn; } /* * Loop through the symbol table entries looking for a match for the * original symbol. */ for (cnt = 0; cnt < symn; syms++, cnt++) { Word shndx; if ((shndx = syms->st_shndx) == SHN_UNDEF) continue; if (osym->st_shndx == SHN_COMMON) { /* * Determine whether a tentative symbol definition * should be overridden. */ if ((shndx == SHN_ABS) || (shndx == SHN_COMMON) || (ELF_ST_TYPE(syms->st_info) == STT_FUNC)) continue; /* * A historic detail requires that a weak definition * within an archive will not override a strong * definition (see sym_realtent() resolution and ABI * symbol binding description - page 4-27). */ if ((ELF_ST_BIND(syms->st_info) == STB_WEAK) && (ELF_ST_BIND(osym->st_info) != STB_WEAK)) continue; } else { /* * Determine whether a restricted visibility reference * should be overridden. Don't worry about the * visibility of the archive member definition, nor * whether it is weak or global. Any definition is * better than a binding to an external shared object * (which is the only event that must presently exist * for us to be here looking for a better alternative). */ if (ELF_ST_TYPE(syms->st_info) != ELF_ST_TYPE(osym->st_info)) continue; } if (strcmp(strs + syms->st_name, name) == 0) return (1); } return (0); }
int main() { Dwarf_Error error = 0; Dwarf_P_Debug dw = dwarf_producer_init_c(DW_DLC_WRITE | DW_DLC_SIZE_64, createSectionCallback, /* error handler */0, /* error arg */0, /* user data */0, &error); if (error != 0) die("dwarf_producer_init_c failed"); Dwarf_Unsigned cie = dwarf_add_frame_cie(dw, "", /* code alignment factor */QT_POINTER_SIZE, /* data alignment factor */-QT_POINTER_SIZE, /* return address reg*/InstructionPointerRegister, cie_init_instructions, sizeof(cie_init_instructions), &error); if (error != 0) die("dwarf_add_frame_cie failed"); Dwarf_P_Fde fde = dwarf_new_fde(dw, &error); if (error != 0) die("dwarf_new_fde failed"); /* New entry in state machine for code offset 1 after push rbp instruction */ dwarf_add_fde_inst(fde, DW_CFA_advance_loc, /*offset in code alignment units*/ 1, /* unused*/ 0, &error); /* After "push rbp" the offset to the CFA is now 2 instead of 1 */ dwarf_add_fde_inst(fde, DW_CFA_def_cfa_offset_sf, /*offset in code alignment units*/ -2, /* unused*/ 0, &error); /* After "push rbp" the value of rbp is now stored at offset 1 from CFA */ dwarf_add_fde_inst(fde, DW_CFA_offset, StackFrameRegister, 2, &error); /* New entry in state machine for code offset 3 for mov rbp, rsp instruction */ dwarf_add_fde_inst(fde, DW_CFA_advance_loc, /*offset in code alignment units*/ 3, /* unused */ 0, &error); /* After "mov rbp, rsp" the cfa is reachable via rbp */ dwarf_add_fde_inst(fde, DW_CFA_def_cfa_register, StackFrameRegister, /* unused */0, &error); /* Callee saved registers */ for (int i = 0; i < calleeSavedRegisterCount; ++i) { dwarf_add_fde_inst(fde, DW_CFA_offset, calleeSavedRegisters[i], i + 3, &error); } dwarf_add_frame_fde(dw, fde, /* die */0, cie, /*virt addr */0, /* length of code */0, /* symbol index */0, &error); if (error != 0) die("dwarf_add_frame_fde failed"); dwarf_transform_to_disk_form(dw, &error); if (error != 0) die("dwarf_transform_to_disk_form failed"); Dwarf_Unsigned len = 0; Dwarf_Signed elfIdx = 0; unsigned char *bytes = (unsigned char *)dwarf_get_section_bytes(dw, /* section */1, &elfIdx, &len, &error); if (error != 0) die("dwarf_get_section_bytes failed"); // libdwarf doesn't add a terminating FDE with zero length, so let's add one // ourselves. unsigned char *newBytes = (unsigned char *)alloca(len + 4); memcpy(newBytes, bytes, len); newBytes[len] = 0; newBytes[len + 1] = 0; newBytes[len + 2] = 0; newBytes[len + 3] = 0; newBytes[len + 4] = 0; bytes = newBytes; len += 4; // Reset CIE-ID back to 0 as expected for .eh_frames bytes[4] = 0; bytes[5] = 0; bytes[6] = 0; bytes[7] = 0; unsigned fde_offset = bytes[0] + 4; bytes[fde_offset + 4] = fde_offset + 4; printf("static const unsigned char cie_fde_data[] = {\n"); int i = 0; while (i < len) { printf(" "); for (int j = 0; i < len && j < 8; ++j, ++i) printf("0x%x, ", bytes[i]); printf("\n"); } printf("};\n"); printf("static const int fde_offset = %d;\n", fde_offset); printf("static const int initial_location_offset = %d;\n", fde_offset + 8); printf("static const int address_range_offset = %d;\n", fde_offset + 8 + QT_POINTER_SIZE); #ifdef DEBUG { if (elf_version(EV_CURRENT) == EV_NONE) die("wrong elf version"); int fd = open("debug.o", O_WRONLY | O_CREAT, 0777); if (fd < 0) die("cannot create debug.o"); Elf *e = elf_begin(fd, ELF_C_WRITE, 0); if (!e) die("elf_begin failed"); Elf_Ehdr *ehdr = elf_newehdr(e); if (!ehdr) die(elf_errmsg(-1)); ehdr->e_ident[EI_DATA] = ELFDATA2LSB; #if defined(Q_PROCESSOR_X86_64) ehdr->e_machine = EM_X86_64; #elif defined(Q_PROCESSOR_X86) ehdr->e_machine = EM_386; #else #error port me :) #endif ehdr->e_type = ET_EXEC; ehdr->e_version = EV_CURRENT; Elf_Scn *section = elf_newscn(e); if (!section) die("elf_newscn failed"); Elf_Data *data = elf_newdata(section); if (!data) die(elf_errmsg(-1)); data->d_align = 4; data->d_off = 0; data->d_buf = bytes; data->d_size = len; data->d_type = ELF_T_BYTE; data->d_version = EV_CURRENT; Elf_Shdr *shdr = elf_getshdr(section); if (!shdr) die(elf_errmsg(-1)); shdr->sh_name = 1; shdr->sh_type = SHT_PROGBITS; shdr->sh_entsize = 0; char stringTable[] = { 0, '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0, '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0 }; section = elf_newscn(e); if (!section) die("elf_newscn failed"); data = elf_newdata(section); if (!data) die(elf_errmsg(-1)); data->d_align = 1; data->d_off = 0; data->d_buf = stringTable; data->d_size = sizeof(stringTable); data->d_type = ELF_T_BYTE; data->d_version = EV_CURRENT; shdr = elf_getshdr(section); if (!shdr) die(elf_errmsg(-1)); shdr->sh_name = 11; shdr->sh_type = SHT_STRTAB; shdr->sh_flags = SHF_STRINGS | SHF_ALLOC; shdr->sh_entsize = 0; ehdr->e_shstrndx = elf_ndxscn(section); if (elf_update(e, ELF_C_WRITE) < 0) die(elf_errmsg(-1)); elf_end(e); close(fd); } #endif dwarf_producer_finish(dw, &error); if (error != 0) die("dwarf_producer_finish failed"); return 0; }
int symtab_init(void) { Elf *elf; Elf_Scn *scn = NULL; Sym *symtab, *symp, *lastsym; char *strtab; uint_t cnt; int fd; int i; int strindex = -1; if ((fd = open("/dev/ksyms", O_RDONLY)) == -1) return (-1); (void) elf_version(EV_CURRENT); elf = elf_begin(fd, ELF_C_READ, NULL); for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) { Shdr *shdr = elf_getshdr(scn); if (shdr->sh_type == SHT_SYMTAB) { symtab = (Sym *)elf_getdata(scn, NULL)->d_buf; nsyms = shdr->sh_size / shdr->sh_entsize; strindex = shdr->sh_link; } } for (cnt = 1; (scn = elf_nextscn(elf, scn)) != NULL; cnt++) { if (cnt == strindex) strtab = (char *)elf_getdata(scn, NULL)->d_buf; } lastsym = symtab + nsyms; nsyms = 0; for (symp = symtab; symp < lastsym; symp++) if ((uint_t)ELF32_ST_TYPE(symp->st_info) <= STT_FUNC && symp->st_size != 0) add_symbol(symp->st_name + strtab, (uintptr_t)symp->st_value, (size_t)symp->st_size); fake_up_certain_popular_kernel_symbols(); (void) sprintf(maxsymname, "0x%lx", ULONG_MAX); add_symbol(maxsymname, ULONG_MAX, 1); qsort(symbol_table, nsyms, sizeof (syment_t), symcmp); /* * Destroy all duplicate symbols, then sort it again. */ for (i = 0; i < nsyms - 1; i++) if (symbol_table[i].addr == symbol_table[i + 1].addr) symbol_table[i].addr = 0; qsort(symbol_table, nsyms, sizeof (syment_t), symcmp); while (symbol_table[1].addr == 0) { symbol_table++; nsyms--; } symbol_table[0].name = "(usermode)"; symbol_table[0].addr = 0; symbol_table[0].size = 1; return (0); }