gcc 8.3 gdb 8.3的安装
我们需要:gmp、mpfr和mpc.还可能需要m4安装工具
编译工具m4 --version查看已经安装的版本,如果过低首先需要升级m4(1.4.18即可)
https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-8.3.0/
https://mirrors.tuna.tsinghua.edu.cn/gnu/gmp/
https://mirrors.tuna.tsinghua.edu.cn/gnu/mpfr/
https://mirrors.tuna.tsinghua.edu.cn/gnu/mpc/
https://mirrors.tuna.tsinghua.edu.cn/gnu/m4/
gcc-8.3.0.tar.gz
mpfr-4.0.2.tar.gz
gmp-6.1.2.tar.xz
mpc-1.0.3.tar.gz
解压,准备依次./configure make make install三部曲.由于各个库之间的依赖关系,安装顺序为m4,gmp,mpfr,mpc
./configure --prefix=/usr/local/gmp
make
make install
./configure --prefix=/usr/local/mpfr --with-gmp=/usr/local/gmp
make
make install
./configure --prefix=/usr/local/mpc --with-gmp=/usr/local/gmp --with-mpfr=/usr/local/mpfr
make
make install
sudo ./configure --prefix=/usr/local/gcc-8.3.0 --with-mpfr=/usr/local/mpfr --with-gmp=/usr/local/gmp --with-mpc=/usr/local/mpc --disable-multilib
make
make install
漫长的编译过程后,在/usr/local/gcc-8.3.0/bin下执行
./gcc -v可以看到版本为8.3以及传递给configure脚本的命令
https://mirrors.ustc.edu.cn/gnu/gdb/
编译gdb并不需要什么依赖,甚至也不需要很高的gcc版本
./configure --prefix=/usr/local/gdb
make
make install
CMake
选择合适版本下载
https://cmake.org/download/
下面是官方教程地址
https://cmake.org/cmake/help/latest/guide/tutorial/index.html
假设我们只有1个源文件ELFAnalyzer.cpp
同目录新建一个CMakeLists.txt如下
cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial ELFAnalyzer.cpp)
执行cmake -G "Unix MakeFile" 即可生成标准的make文件然后make即可得到可执行文件Tutorial
cmake --help可以看到支持的文件格式
gcc生成标准链接库
需要-fPIC选项
则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
假设有source1.cpp
int add(int a,int b){
return a+b;
}
首先生成位置无关目标文件
gcc -fPIC -c source1.cpp
默认生成文件名为source1.o
生成so共享库文件
gcc -shared -o libsource1.so source1.o
使用动态链接库需要-L指定寻找动态链接库文件的路径(可以多个)
g++ main.cpp -L./ -lsource1 -o a.out
这样生成了可执行文件a.out但是如果我们直接./a.out运行它会报找不到动态库文件.
error while loading shared libraries
默认会去/usr/lib下寻找动态链接库文件
ldd a.out可以看这个文件需要哪些库
可以执行
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
来将当前路径加入动态库搜索路径
再执行./a.out即可打印3
名称修饰规则
https://github.com/gchatelet/gcc_cpp_mangling_documentation
https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
https://stackoverflow.com/questions/6921295/dual-emission-of-constructor-symbols
能输出自己的程序.参考文档
https://www.eecs.umich.edu/courses/eecs373/readings/Assembler.pdf
https://www.eecs.umich.edu/courses/eecs373/readings/Linker.pdf
include <stdio.h>
extern const char kCurSourceFile[];
asm(".section .rodata, "a", @progbits\n");
asm("kCurSourceFile:\n");
asm(".incbin "a.cpp"\n");
asm(".byte 0\n");
asm(".previous\n");
int main()
{
printf("%s", kCurSourceFile);
return 0;
}
关于3目运算符的返回值类型
对于cpp,返回值必须有公共的范围(也就是取范围较大的),不能返回指向父类的指针
#include <stdio.h>
#include <stdlib.h>
int f1() { return 2147483647; }
long f2() { return 2147483648; }
class A
{
};
class B : public A
{
};
class C : public A
{
};
int main()
{
srand(time(0)); auto a = rand() % 2 ? B() : C(); //incompatible operand types ('B' and 'C') A *b = rand() % 2 ? B() : C(); //incompatible operand types ('B' and 'C') for (int i = 0; i < 30; i++) { int b = rand() % 2 ? f1() : f2(); printf("%d\n", b); } //OK.如果f2被执行则打印-2147483648.事实上返回的是long类型 auto b = rand() % 2 ? f1() : f2(); printf("%u\n", sizeof(b)); //打印8说明推断类型为long return 0;
}
elf文件解析
首先是Elf header(对比DOS header)
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info /
Elf64_Half e_type; / Object file type /
Elf64_Half e_machine; / Architecture /
Elf64_Word e_version; / Object file version /
Elf64_Addr e_entry; / Entry point virtual address /
Elf64_Off e_phoff; / Program header table file offset /
Elf64_Off e_shoff; / Section header table file offset /
Elf64_Word e_flags; / Processor-specific flags /
Elf64_Half e_ehsize; / ELF header size in bytes /
Elf64_Half e_phentsize; / Program header table entry size /
Elf64_Half e_phnum; / Program header table entry count /
Elf64_Half e_shentsize; / Section header table entry size /
Elf64_Half e_shnum; / Section header table entry count /
Elf64_Half e_shstrndx; / Section header string table index */
} Elf64_Ehdr;
e_phoff,e_shoff分别记录了program header table和section header table在文件中的偏移量.e_shstrndx是记录了section header table中各个表项名称的那个section的index
通常来说section header table位于文件的末尾处.每个表项结构如下.sh_name表示了它在e_shstrndx这一节中的偏移量.e_shstrndx这一节的内容就是若干个以0结尾的字符串,其中第0个字符串总是空串(只有1个0).这个节名字表自己的名称一般是.shstrtab
typedef struct
{
Elf64_Word sh_name; /* Section name (string tbl index) /
Elf64_Word sh_type; / Section type /
Elf64_Xword sh_flags; / Section flags /
Elf64_Addr sh_addr; / Section virtual addr at execution /
Elf64_Off sh_offset; / Section file offset /
Elf64_Xword sh_size; / Section size in bytes /
Elf64_Word sh_link; / Link to another section /
Elf64_Word sh_info; / Additional section information /
Elf64_Xword sh_addralign; / Section alignment /
Elf64_Xword sh_entsize; / Entry size if section holds table */
} Elf64_Shdr;
sh_type表示了这一节的类型
第0个section的类型永远是SHT_NULL.注意如果elf header中e_shnum这一项为0则实际的section header表项数由第0个section header的sh_size成员给出.
我们主要关心SHT_REL,SHT_RELA(relocation addend)类型的重定位节
SHT_SYMTAB符号表SHT_STRTAB字符串表.
如果某个section实际上是一个表,由于这个section的总大小为sh_size而每个表项的大小为sh_entsize所以总共有
sh_size/sh_entsize个表项.比如重定位表就是这样.如果这个section实际上并不是一个表则sh_entsize=0
对于符号表来说,部分符号有名称,需要一个字符串表保存这些符号名称.这个字符串表的index就由sh_link字段给出.
通常来说,每个重定位节对应于一个要被他重定位的节.假设.text节要被重定位,则记录对应重定位信息的节名称为.rel.text或者.rela.text
sh_addralign字段给出了本节的对齐数,会保证本节在文件中的偏移地址是这个值的倍数.
#define SHT_NULL 0 /* Section header table entry unused /
#define SHT_PROGBITS 1 / Program data /
#define SHT_SYMTAB 2 / Symbol table /
#define SHT_STRTAB 3 / String table /
#define SHT_RELA 4 / Relocation entries with addends /
#define SHT_HASH 5 / Symbol hash table /
#define SHT_DYNAMIC 6 / Dynamic linking information /
#define SHT_NOTE 7 / Notes /
#define SHT_NOBITS 8 / Program space with no data (bss) /
#define SHT_REL 9 / Relocation entries, no addends /
#define SHT_SHLIB 10 / Reserved /
#define SHT_DYNSYM 11 / Dynamic linker symbol table /
#define SHT_INIT_ARRAY 14 / Array of constructors /
#define SHT_FINI_ARRAY 15 / Array of destructors /
#define SHT_PREINIT_ARRAY 16 / Array of pre-constructors /
#define SHT_GROUP 17 / Section group /
#define SHT_SYMTAB_SHNDX 18 / Extended section indeces /
#define SHT_NUM 19 / Number of defined types. /
#define SHT_LOOS 0x60000000 / Start OS-specific. /
#define SHT_GNU_ATTRIBUTES 0x6ffffff5 / Object attributes. /
#define SHT_GNU_HASH 0x6ffffff6 / GNU-style hash table. /
#define SHT_GNU_LIBLIST 0x6ffffff7 / Prelink library list /
#define SHT_CHECKSUM 0x6ffffff8 / Checksum for DSO content. /
#define SHT_LOSUNW 0x6ffffffa / Sun-specific low bound. /
#define SHT_SUNW_move 0x6ffffffa
#define SHT_SUNW_COMDAT 0x6ffffffb
#define SHT_SUNW_syminfo 0x6ffffffc
#define SHT_GNU_verdef 0x6ffffffd / Version definition section. /
#define SHT_GNU_verneed 0x6ffffffe / Version needs section. /
#define SHT_GNU_versym 0x6fffffff / Version symbol table. /
#define SHT_HISUNW 0x6fffffff / Sun-specific high bound. /
#define SHT_HIOS 0x6fffffff / End OS-specific type /
#define SHT_LOPROC 0x70000000 / Start of processor-specific /
#define SHT_HIPROC 0x7fffffff / End of processor-specific /
#define SHT_LOUSER 0x80000000 / Start of application-specific /
#define SHT_HIUSER 0x8fffffff / End of application-specific */
/* Legal values for sh_flags (section flags). */
define SHF_WRITE (1 << 0) /* Writable */
define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
define SHF_EXECINSTR (1 << 2) /* Executable */
define SHF_MERGE (1 << 4) /* Might be merged */
define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */
define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */
define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */
define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling
required */
define SHF_GROUP (1 << 9) /* Section is member of a group. */
define SHF_TLS (1 << 10) /* Section hold thread-local data. */
define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */
define SHF_MASKOS 0x0ff00000 /* OS-specific. */
define SHF_MASKPROC 0xf0000000 /* Processor-specific */
define SHF_ORDERED (1 << 30) /* Special ordering requirement
(Solaris). */
define SHF_EXCLUDE (1U << 31) /* Section is excluded unless
referenced or allocated (Solaris).*/
typedef struct
{
Elf64_Word p_type; /* Segment type /
Elf64_Word p_flags; / Segment flags /
Elf64_Off p_offset; / Segment file offset /
Elf64_Addr p_vaddr; / Segment virtual address /
Elf64_Addr p_paddr; / Segment physical address /
Elf64_Xword p_filesz; / Segment size in file /
Elf64_Xword p_memsz; / Segment size in memory /
Elf64_Xword p_align; / Segment alignment */
} Elf64_Phdr;
include <stdlib.h>
include <sys/stat.h>
include <unistd.h>
include
include <string.h>
include
include "/usr/include/elf.h"
void print_elf_header_info(const Elf64_Ehdr &elf_header);
void print_section_header_info(const std::vector<elf64_shdr> &Elf64_Shdr_vec, const Elf64_Shdr &name_section, const char *file_buffer, std::vector<int> &symbol_table_index, std::vector<int> &relocation_index, std::vector<int> &relocation_addend_index);
void print_symbol_table_info(const char *file_buffer, Elf64_Off sh_offset, int entry_count, const Elf64_Shdr &string_section_header);
void print_relocation_table_info(const char *file_buffer, Elf64_Off sh_offset, int entry_count);
void print_relocation_addend_table_info(const char *file_buffer, Elf64_Off sh_offset, int entry_count);
void print_relocation_type_info(int type);
void print_program_header_table_info(Elf64_Phdr program_header);</int></int></int></elf64_shdr>
std::string concat_str(const std::vectorstd::string &vec)
{
if (vec.empty())
return "";
std::string s;
for (size_t i = 0; i < vec.size() - 1; i++)
s += vec.at(i) + "|";
s += vec.at(vec.size() - 1);
return s;
}
int main(int argc, const char *argv[])
{
const char *file_path = "/home/tangsong.math/repos/a.out";
if (argc >= 2)
file_path = argv[1];
FILE *fp = fopen(file_path, "r");
if (!fp)
{
printf("file not found\n");
return 0;
}
struct stat file_stat;
memset(&file_stat, 0, sizeof(struct stat)); stat(file_path, &file_stat); mode_t file_mode = file_stat.st_mode; std::vector<std::string> file_mode_vec; if (S_ISLNK(file_mode)) file_mode_vec.push_back("S_IFLNK"); if (S_ISREG(file_mode)) file_mode_vec.push_back("S_IFREG"); if (S_ISBLK(file_mode)) file_mode_vec.push_back("S_IFBLK"); if (S_ISDIR(file_mode)) file_mode_vec.push_back("S_IFDIR"); if (S_ISFIFO(file_mode)) file_mode_vec.push_back("S_IFIFO"); if (file_mode & S_ISUID) file_mode_vec.push_back("S_ISUID"); if (file_mode & S_ISGID) file_mode_vec.push_back("S_ISGID"); if (file_mode & S_ISVTX) file_mode_vec.push_back("S_ISVTX"); if (file_mode & S_IRUSR) file_mode_vec.push_back("S_IRUSR"); if (file_mode & S_IWUSR) file_mode_vec.push_back("S_IWUSR"); if (file_mode & S_IXUSR) file_mode_vec.push_back("S_IXUSR"); if (file_mode & S_IRGRP) file_mode_vec.push_back("S_IRGRP"); if (file_mode & S_IWGRP) file_mode_vec.push_back("S_IWGRP"); if (file_mode & S_IXGRP) file_mode_vec.push_back("S_IXGRP"); if (file_mode & S_IROTH) file_mode_vec.push_back("S_IROTH"); if (file_mode & S_IWOTH) file_mode_vec.push_back("S_IWOTH"); if (file_mode & S_IXOTH) file_mode_vec.push_back("S_IXOTH"); printf("file_mode %d %s\n", file_mode, concat_str(file_mode_vec).c_str()); if (!S_ISREG(file_mode)) { printf("not a regular file\n"); return 0; } //off_t在macOS是long long off_t file_size = file_stat.st_size; printf("file_size = %d\n\n", file_size); if (file_size < sizeof(Elf64_Ehdr)) { printf("not a valid elf64 file\n"); return 0; } char *file_buffer = new char[file_size]; //analyze start fread(file_buffer, file_size, 1, fp); Elf64_Ehdr elf_header = *(Elf64_Ehdr *)file_buffer; print_elf_header_info(elf_header); //读取section header table //在此过程中寻找所有的字符串section.也就是sh_type字段=SHT_STRTAB的section header std::vector<Elf64_Shdr> Elf64_Shdr_vec; std::vector<Elf64_Shdr> Elf64_SHT_STRTAB_vec; std::vector<Elf64_Phdr> Elf64_Phdr_vec; Elf64_Shdr *p_section_header = (Elf64_Shdr *)(file_buffer + elf_header.e_shoff); Elf64_Phdr *p_program_header = (Elf64_Phdr *)(file_buffer + elf_header.e_phoff); int section_header_count = elf_header.e_shnum ? elf_header.e_shnum : p_section_header[0].sh_size; int non_null_section_header_count = 0; for (int i = 0; i < section_header_count; i++) { Elf64_Shdr_vec.push_back(*p_section_header); if (p_section_header->sh_type == SHT_STRTAB) Elf64_SHT_STRTAB_vec.push_back(*p_section_header); if (p_section_header->sh_type != SHT_NULL) non_null_section_header_count++; p_section_header++; } for (int i = 0; i < elf_header.e_phnum; i++) { Elf64_Phdr_vec.push_back(*p_program_header); p_program_header++; } Elf64_Shdr section_header_name_section = Elf64_Shdr_vec.at(elf_header.e_shstrndx); printf("\n\nsection header table count %d\n", section_header_count); printf("SHT_STRTAB section count %d\n", Elf64_SHT_STRTAB_vec.size()); printf("non null section count %d\n", non_null_section_header_count); std::vector<int> symbol_table_index; std::vector<int> relocation_index; std::vector<int> relocation_addend_index; print_section_header_info(Elf64_Shdr_vec, section_header_name_section, file_buffer, symbol_table_index, relocation_index, relocation_addend_index); for (int i = 0; i < symbol_table_index.size(); i++) { printf("symbol table %d info\n", i); Elf64_Word string_section_header_index = Elf64_Shdr_vec.at(symbol_table_index[i]).sh_link; Elf64_Shdr string_section_header = Elf64_Shdr_vec.at(string_section_header_index); int entry_count = Elf64_Shdr_vec.at(symbol_table_index[i]).sh_size / Elf64_Shdr_vec.at(symbol_table_index[i]).sh_entsize; print_symbol_table_info(file_buffer, Elf64_Shdr_vec.at(symbol_table_index[i]).sh_offset, entry_count, string_section_header); } printf("\n"); printf("relocation section count %d\n", relocation_index.size()); for (int i = 0; i < relocation_index.size(); i++) { printf("relocation table %d info\n", i); int entry_count = Elf64_Shdr_vec.at(relocation_index[i]).sh_size / Elf64_Shdr_vec.at(relocation_index[i]).sh_entsize; print_relocation_table_info(file_buffer, Elf64_Shdr_vec.at(relocation_index[i]).sh_offset, entry_count); } printf("\n"); printf("relocation addend section count %d\n", relocation_addend_index.size()); for (int i = 0; i < relocation_addend_index.size(); i++) { printf("relocation addend table %d info\n", i); int entry_count = Elf64_Shdr_vec.at(relocation_addend_index[i]).sh_size / Elf64_Shdr_vec.at(relocation_addend_index[i]).sh_entsize; print_relocation_addend_table_info(file_buffer, Elf64_Shdr_vec.at(relocation_addend_index[i]).sh_offset, entry_count); } printf("\n\nprogram header table count %d\n", Elf64_Phdr_vec.size()); for (int i = 0; i < Elf64_Phdr_vec.size(); i++) { printf("[%d. ]", i); print_program_header_table_info(Elf64_Phdr_vec.at(i)); } delete[] file_buffer; return 0;
}
void print_program_header_table_info(Elf64_Phdr program_header)
{
printf("p_type = %d ", program_header.p_type);
switch (program_header.p_type)
{
case PT_NULL:
printf("PT_NULL ");
break;
case PT_LOAD:
printf("PT_LOAD ");
break;
case PT_DYNAMIC:
printf("PT_DYNAMIC ");
break;
case PT_INTERP:
printf("PT_INTERP ");
break;
case PT_NOTE:
printf("PT_NOTE ");
break;
case PT_SHLIB:
printf("PT_SHLIB ");
break;
case PT_PHDR:
printf("PT_PHDR ");
break;
case PT_TLS:
printf("PT_TLS ");
break;
}
if (PT_LOOS <= program_header.p_type && program_header.p_type <= PT_HIOS)
{
switch (program_header.p_type)
{
case PT_GNU_EH_FRAME:
printf("PT_GNU_EH_FRAME ");
break;
case PT_GNU_STACK:
printf("PT_GNU_STACK ");
break;
case PT_GNU_RELRO:
printf("PT_GNU_RELRO ");
break;
default:
printf("OS-specific");
break;
}
}
printf("\n"); std::vector<std::string> p_flags_vec; if (program_header.p_flags & PF_X) p_flags_vec.push_back("PF_X"); if (program_header.p_flags & PF_W) p_flags_vec.push_back("PF_W"); if (program_header.p_flags & PF_R) p_flags_vec.push_back("PF_R"); printf("p_flags = %d %s\n", program_header.p_flags, concat_str(p_flags_vec).c_str()); printf("p_offset = %lld\n ", program_header.p_offset); printf("p_vaddr = %lld \n", program_header.p_vaddr); printf("p_paddr = %lld \n", program_header.p_paddr); printf("p_filesz = %lld \n", program_header.p_filesz); printf("p_memsz = %lld \n", program_header.p_memsz); printf("p_align = %lld \n\n", program_header.p_align);
}
void print_relocation_table_info(const char *file_buffer, Elf64_Off sh_offset, int entry_count)
{
Elf64_Rel *p_rel = (Elf64_Rel *)(file_buffer + sh_offset);
printf("entry_count= %d\n", entry_count);
for (int i = 0; i < entry_count; i++)
{
printf("[%d. ]", i);
printf("r_offset = %d ", p_rel->r_offset);
printf("r_info = %lld ", p_rel->r_info);
print_relocation_type_info(ELF64_R_TYPE(p_rel->r_info));
p_rel++; }
}
void print_relocation_addend_table_info(const char *file_buffer, Elf64_Off sh_offset, int entry_count)
{
Elf64_Rela *p_rela = (Elf64_Rela *)(file_buffer + sh_offset);
printf("entry_count= %d\n", entry_count);
for (int i = 0; i < entry_count; i++)
{
printf("[%d. ]", i);
printf("r_offset = %lld ", p_rela->r_offset);
printf("r_info ELF64_R_SYM=%d ELF64_R_TYPE=%d ", ELF64_R_SYM(p_rela->r_info), ELF64_R_TYPE(p_rela->r_info));
print_relocation_type_info(ELF64_R_TYPE(p_rela->r_info));
printf("r_addend = %lld \n", p_rela->r_addend); p_rela++; }
}
void print_relocation_type_info(int type)
{
switch (type) { case R_386_NONE: printf("R_386_NONE "); break; case R_386_32: printf("R_386_32 "); break; case R_386_PC32: printf("R_386_PC32 "); break; case R_386_GOT32: printf("R_386_GOT32 "); break; case R_386_PLT32: printf("R_386_PLT32 "); break; case R_386_COPY: printf("R_386_COPY "); break; case R_386_GLOB_DAT: printf("R_386_GLOB_DAT "); break; case R_386_JMP_SLOT: printf("R_386_JMP_SLOT "); break; case R_386_RELATIVE: printf("R_386_RELATIVE "); break; case R_386_GOTOFF: printf("R_386_GOTOFF "); break; case R_386_GOTPC: printf("R_386_GOTPC "); break; }
}
void print_symbol_table_info(const char *file_buffer, Elf64_Off sh_offset, int entry_count, const Elf64_Shdr &string_section_header)
{
Elf64_Sym *p_symbol_table = (Elf64_Sym *)(file_buffer + sh_offset);
printf("entry_count= %d\n", entry_count);
for (int i = 0; i < entry_count; i++)
{
printf("[%d. ]", i);
printf("st_name = %s \n", file_buffer + string_section_header.sh_offset + p_symbol_table->st_name);
printf("st_info = %d ", (int)p_symbol_table->st_info);
switch (ELF64_ST_BIND(p_symbol_table->st_info))
{
case STB_LOCAL:
printf("STB_LOCAL ");
break;
case STB_GLOBAL:
printf("STB_GLOBAL ");
break;
case STB_WEAK:
printf("STB_WEAK ");
break;
}
switch (ELF64_ST_TYPE(p_symbol_table->st_info))
{
case STT_NOTYPE: printf("STT_NOTYPE "); break; case STT_OBJECT: printf("STT_OBJECT "); break; case STT_FUNC: printf("STT_FUNC "); break; case STT_SECTION: printf("STT_SECTION "); break; case STT_FILE: printf("STT_FILE "); break; case STT_COMMON: printf("STT_COMMON "); break; case STT_TLS: printf("STT_TLS "); break; } printf("st_other = %d ", (int)p_symbol_table->st_other); printf("st_value = %d ", p_symbol_table->st_value); printf("st_size = %lld\n", p_symbol_table->st_size); p_symbol_table++; }
}
void print_section_header_info(const std::vector<elf64_shdr> &Elf64_Shdr_vec, const Elf64_Shdr §ion_header_name_section, const char *file_buffer, std::vector<int> &symbol_table_index,
std::vector<int> &relocation_index, std::vector<int> &relocation_addend_index)
{</int></int></int></elf64_shdr>
for (int i = 0; i < Elf64_Shdr_vec.size(); i++) { Elf64_Shdr section_header = Elf64_Shdr_vec.at(i); printf("[%d. ] ", i); printf("sh_name = %d %s ", section_header.sh_name, file_buffer + section_header_name_section.sh_offset + section_header.sh_name); printf("sh_type = %d ", section_header.sh_type); switch (section_header.sh_type) { case SHT_NULL: printf("SHT_NULL\n"); break; case SHT_PROGBITS: printf("SHT_PROGBITS\n"); break; case SHT_SYMTAB: printf("SHT_SYMTAB\n"); symbol_table_index.push_back(i); break; case SHT_STRTAB: printf("SHT_STRTAB\n"); break; case SHT_RELA: printf("SHT_RELA\n"); relocation_addend_index.push_back(i); break; case SHT_HASH: printf("SHT_HASH\n"); break; case SHT_DYNAMIC: printf("SHT_DYNAMIC\n"); break; case SHT_NOTE: printf("SHT_NOTE\n"); break; case SHT_NOBITS: printf("SHT_NOBITS\n"); break; case SHT_REL: printf("SHT_REL\n"); relocation_index.push_back(i); break; case SHT_SHLIB: printf("SHT_SHLIB\n"); break; case SHT_DYNSYM: printf("SHT_DYNSYM\n"); break; case SHT_INIT_ARRAY: printf("SHT_INIT_ARRAY\n"); break; case SHT_FINI_ARRAY: printf("SHT_FINI_ARRAY\n"); break; case SHT_PREINIT_ARRAY: printf("SHT_PREINIT_ARRAY\n"); break; case SHT_GROUP: printf("SHT_GROUP\n"); break; case SHT_SYMTAB_SHNDX: printf("SHT_SYMTAB_SHNDX\n"); break; } if (SHT_LOOS <= section_header.sh_type && section_header.sh_type <= SHT_HIOS) printf("OS-specific type\n"); //typedef uint64_t Elf64_Xword; printf("sh_flags = %lld ", section_header.sh_flags); std::vector<std::string> sh_flags_vec; if (section_header.sh_flags & SHF_WRITE) sh_flags_vec.push_back("SHF_WRITE"); if (section_header.sh_flags & SHF_ALLOC) sh_flags_vec.push_back("SHF_ALLOC"); if (section_header.sh_flags & SHF_EXECINSTR) sh_flags_vec.push_back("SHF_EXECINSTR"); if (section_header.sh_flags & SHF_MERGE) sh_flags_vec.push_back("SHF_MERGE"); if (section_header.sh_flags & SHF_STRINGS) sh_flags_vec.push_back("SHF_STRINGS"); if (section_header.sh_flags & SHF_INFO_LINK) sh_flags_vec.push_back("SHF_INFO_LINK"); if (section_header.sh_flags & SHF_LINK_ORDER) sh_flags_vec.push_back("SHF_LINK_ORDER"); if (section_header.sh_flags & SHF_OS_NONCONFORMING) sh_flags_vec.push_back("SHF_OS_NONCONFORMING"); if (section_header.sh_flags & SHF_GROUP) sh_flags_vec.push_back("SHF_GROUP"); if (section_header.sh_flags & SHF_TLS) sh_flags_vec.push_back("SHF_TLS"); if (section_header.sh_flags & SHF_COMPRESSED) sh_flags_vec.push_back("SHF_COMPRESSED"); if (section_header.sh_flags & SHF_MASKOS) sh_flags_vec.push_back("SHF_MASKOS"); if (section_header.sh_flags & SHF_MASKPROC) sh_flags_vec.push_back("SHF_MASKPROC"); if (section_header.sh_flags & SHF_ORDERED) sh_flags_vec.push_back("SHF_ORDERED"); if (section_header.sh_flags & SHF_EXCLUDE) sh_flags_vec.push_back("SHF_EXCLUDE"); printf("%s\n", concat_str(sh_flags_vec).c_str()); printf("sh_addr = %d\n", section_header.sh_addr); printf("sh_offset = %lld\n", section_header.sh_offset); printf("sh_size = %lld\n", section_header.sh_size); printf("sh_link = %d\n", section_header.sh_link); printf("sh_info = %d\n", section_header.sh_info); printf("sh_addralign = %lld\n", section_header.sh_addralign); printf("sh_entsize = %d\n\n", section_header.sh_entsize); }
}
void print_elf_header_info(const Elf64_Ehdr &elf_header)
{
printf("Elf64_Ehdr info:\n"); //ELF Identification printf("e_ident[EI_NIDENT=%d] = ", EI_NIDENT); for (int i = 0; i < EI_NIDENT; i++) printf("%x ", elf_header.e_ident[i]); printf("Magic Number = "); for (int i = 0; i < 4; i++) printf("%x ", elf_header.e_ident[i]); printf("\nEI_CLASS %d ", (int)elf_header.e_ident[EI_CLASS]); //EI_CLASS=4 switch (elf_header.e_ident[EI_CLASS]) { case ELFCLASSNONE: printf("ELFCLASSNONE\n"); break; case ELFCLASS32: printf("ELFCLASS32\n"); break; case ELFCLASS64: printf("ELFCLASS64\n"); break; case ELFCLASSNUM: printf("ELFCLASSNUM\n"); break; default: printf("unspecified EI_CLASS\n"); break; } printf("EI_DATA %d ", (int)elf_header.e_ident[EI_DATA]); //EI_DATA=5 switch (elf_header.e_ident[EI_DATA]) { case ELFDATANONE: printf("ELFCLASSNONE\n"); break; case ELFDATA2LSB: printf("ELFDATA2LSB\n"); break; case ELFDATA2MSB: printf("ELFDATA2MSB\n"); break; default: printf("unspecified EI_DATA\n"); break; } printf("EI_VERSION %d ", (int)elf_header.e_ident[EI_VERSION]); //EI_DATA=6 switch (elf_header.e_ident[EI_VERSION]) { case EV_CURRENT: printf("EV_CURRENT\n"); break; default: printf("unspecified EI_VERSION\n"); break; } printf("EI_OSABI %d ", (int)elf_header.e_ident[EI_OSABI]); //EI_OSABI=7 switch (elf_header.e_ident[EI_OSABI]) { case ELFOSABI_NONE: printf("ELFOSABI_NONE\n"); break; case ELFOSABI_HPUX: printf("ELFOSABI_HPUX\n"); break; case ELFOSABI_NETBSD: printf("ELFOSABI_NETBSD\n"); break; case ELFOSABI_GNU: printf("ELFOSABI_GNU\n"); break; case ELFOSABI_SOLARIS: printf("ELFOSABI_SOLARIS\n"); break; case ELFOSABI_AIX: printf("ELFOSABI_AIX\n"); break; case ELFOSABI_IRIX: printf("ELFOSABI_IRIX\n"); break; case ELFOSABI_FREEBSD: printf("ELFOSABI_FREEBSD\n"); break; case ELFOSABI_TRU64: printf("ELFOSABI_TRU64\n"); break; case ELFOSABI_MODESTO: printf("ELFOSABI_MODESTO\n"); break; case ELFOSABI_OPENBSD: printf("ELFOSABI_OPENBSD\n"); break; case ELFOSABI_ARM_AEABI: printf("ELFOSABI_ARM_AEABI\n"); break; case ELFOSABI_ARM: printf("ELFOSABI_ARM\n"); break; case ELFOSABI_STANDALONE: printf("ELFOSABI_STANDALONE\n"); break; default: printf("unspecified EI_OSABI\n"); break; } printf("EI_ABIVERSION %d ", (int)elf_header.e_ident[EI_ABIVERSION]); //EI_DATA=6 switch (elf_header.e_ident[EI_ABIVERSION]) { case 0: printf("\n"); break; default: printf("unspecified EI_ABIVERSION\n"); break; } printf("EI_PAD %d ", (int)elf_header.e_ident[EI_PAD]); //EI_DATA=6 switch (elf_header.e_ident[EI_PAD]) { case 0: printf("\n"); break; default: printf("unspecified EI_PAD\n"); break; } //typedef uint16_t Elf64_Half; Elf64_Half e_type = elf_header.e_type; printf("e_type %d ", e_type); switch (e_type) { case ET_NONE: printf("ET_NONE No file type\n"); break; case ET_REL: printf("ET_REL Relocatable file\n"); break; case ET_DYN: printf("ET_DYN Shared object file\n"); break; case ET_CORE: printf("ET_CORE Core file\n"); break; } if (ET_LOOS <= e_type && e_type <= ET_HIOS) printf("OS-specific\n"); if (ET_LOPROC <= e_type && e_type <= ET_HIPROC) printf("Processor-specific\n"); Elf64_Half e_machine = elf_header.e_machine; printf("e_machine %d ", e_machine); switch (e_machine) { case EM_NONE: printf("EM_NONE No machine\n"); break; case EM_X86_64: printf("EM_X86_64 AMD x86-64 architecture\n"); break; //other machine types are omitted. } //typedef uint32_t Elf32_Word; Elf64_Word e_version = elf_header.e_version; printf("e_version %d ", e_version); switch (e_version) { case EV_NONE: printf("EV_NONE Invalid ELF version\n"); break; case EV_CURRENT: printf("EV_CURRENT Current version\n"); break; default: printf("unspecified e_version\n"); break; } //typedef uint32_t Elf32_Addr; Elf32_Addr e_entry = elf_header.e_entry; printf("e_entry %d Entry point virtual address\n", e_entry); Elf32_Off e_phoff = elf_header.e_phoff; printf("e_phoff %d Program header table file offset\n", e_phoff); Elf32_Off e_shoff = elf_header.e_shoff; printf("e_shoff %d Section header table file offset\n", e_shoff); Elf32_Word e_flags = elf_header.e_flags; printf("e_flags %d Processor-specific flags\n", e_flags); Elf64_Half e_ehsize = elf_header.e_ehsize; printf("e_ehsize %d ELF header size in bytes\n", e_ehsize); Elf64_Half e_phentsize = elf_header.e_phentsize; printf("e_phentsize %d Program header table entry size\n", e_phentsize); Elf64_Half e_phnum = elf_header.e_phnum; printf("e_phnum %d Program header table entry count\n", e_phnum); Elf64_Half e_shentsize = elf_header.e_shentsize; printf("e_shentsize %d Section header table entry size\n", e_shentsize); Elf64_Half e_shnum = elf_header.e_shnum; printf("e_shnum %d Section header table entry count\n", e_shnum); Elf64_Half e_shstrndx = elf_header.e_shstrndx; printf("e_shstrndx %d Section header string table index\n", e_shstrndx);
}
CMake
选择合适版本下载
https://cmake.org/download/
下面是官方教程地址
https://cmake.org/cmake/help/latest/guide/tutorial/index.html
假设我们只有1个源文件ELFAnalyzer.cpp
同目录新建一个CMakeLists.txt如下
cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial ELFAnalyzer.cpp)
执行cmake -G "Unix MakeFile" 即可生成标准的make文件然后make即可得到可执行文件Tutorial
cmake --help可以看到支持的文件格式
gcc生成标准链接库
需要-fPIC选项
则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
假设有source1.cpp
int add(int a,int b){
return a+b;
}
首先生成位置无关目标文件
gcc -fPIC -c source1.cpp
默认生成文件名为source1.o
生成so共享库文件
gcc -shared -o libsource1.so source1.o
使用动态链接库需要-L指定寻找动态链接库文件的路径(可以多个)
g++ main.cpp -L./ -lsource1 -o a.out
这样生成了可执行文件a.out但是如果我们直接./a.out运行它会报找不到动态库文件.
error while loading shared libraries
默认会去/usr/lib下寻找动态链接库文件
ldd a.out可以看这个文件需要哪些库
可以执行
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
来将当前路径加入动态库搜索路径
再执行./a.out即可打印3
关于3目运算符的返回值类型
对于cpp,返回值必须有公共的范围(也就是取范围较大的),不能返回指向父类的指针
include <stdio.h>
include <stdlib.h>
int f1() { return 2147483647; }
long f2() { return 2147483648; }
class A
{
};
class B : public A
{
};
class C : public A
{
};
int main()
{
srand(time(0)); auto a = rand() % 2 ? B() : C(); //incompatible operand types ('B' and 'C') A *b = rand() % 2 ? B() : C(); //incompatible operand types ('B' and 'C') for (int i = 0; i < 30; i++) { int b = rand() % 2 ? f1() : f2(); printf("%d\n", b); } //OK.如果f2被执行则打印-2147483648.事实上返回的是long类型 auto b = rand() % 2 ? f1() : f2(); printf("%u\n", sizeof(b)); //打印8说明推断类型为long return 0;
}
Java中可以
import java.util.*;
public class Foo {
public static void main(String[] args) throws Exception { Random r = new Random(); A a = r.nextInt() % 2 == 0 ? new B() : new C(); }
}
class A {
}
class B extends A {
}
class C extends A {
}
- 内存占用率高问题排查
top命令
统计信息区:
前五行是当前系统情况整体的统计信息区。下面我们看每一行信息的具体意义。
第一行,任务队列信息,同 uptime 命令的执行结果,具体参数说明情况如下:
14:06:23 — 当前系统时间
up 70 days, 16:44 — 系统已经运行了70天16小时44分钟(在这期间系统没有重启过的吆!)
2 users — 当前有2个用户登录系统
load average: 1.15, 1.42, 1.44 — load average后面的三个数分别是1分钟、5分钟、15分钟的负载情况。
load average数据是每隔5秒钟检查一次活跃的进程数,然后按特定算法计算出的数值。如果这个数除以逻辑CPU的数量,结果高于5的时候就表明系统在超负荷运转了。
第二行,Tasks — 任务(进程),具体信息说明如下:
系统现在共有206个进程,其中处于运行中的有1个,205个在休眠(sleep),stoped状态的有0个,zombie状态(僵尸)的有0个。
第三行,cpu状态信息,具体属性说明如下: - 9%us — 用户空间占用CPU的百分比。
- 4% sy — 内核空间占用CPU的百分比。
- 0% ni — 改变过优先级的进程占用CPU的百分比
- 4% id — 空闲CPU百分比
- 0% wa — IO等待占用CPU的百分比
- 0% hi — 硬中断(Hardware IRQ)占用CPU的百分比
- 2% si — 软中断(Software Interrupts)占用CPU的百分比
备注:在这里CPU的使用比率和windows概念不同,需要理解linux系统用户空间和内核空间的相关知识!
第四行,内存状态,具体信息如下:
32949016k total — 物理内存总量(32GB)
14411180k used — 使用中的内存总量(14GB)
18537836k free — 空闲内存总量(18GB)
169884k buffers — 缓存的内存量 (169M)
第五行,swap交换分区信息,具体信息说明如下:
32764556k total — 交换区总量(32GB)
0k used — 使用的交换区总量(0K)
32764556k free — 空闲交换区总量(32GB)
3612636k cached — 缓冲的交换区总量(3.6GB)
备注:
第四行中使用中的内存总量(used)指的是现在系统内核控制的内存数,空闲内存总量(free)是内核还未纳入其管控范围的数量。纳入内核管理的内存不见得都在使用中,还包括过去使用过的现在可以被重复利用的内存,内核并不把这些可被重新使用的内存交还到free中去,因此在linux上free内存会越来越少,但不用为此担心。
如果出于习惯去计算可用内存数,这里有个近似的计算公式:第四行的free + 第四行的buffers + 第五行的cached,按这个公式此台服务器的可用内存:18537836k +169884k +3612636k = 22GB左右。
对于内存监控,在top里我们要时刻监控第五行swap交换分区的used,如果这个数值在不断的变化,说明内核在不断进行内存和swap的数据交换,这是真正的内存不够用了。
第六行,空行。
第七行以下:各进程(任务)的状态监控,项目列信息说明如下:
PID — 进程id
USER — 进程所有者
PR — 进程优先级
NI — nice值。负值表示高优先级,正值表示低优先级
VIRT — 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
RES — 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
SHR — 共享内存大小,单位kb
S — 进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程
%CPU — 上次更新到现在的CPU时间占用百分比
%MEM — 进程使用的物理内存百分比
TIME+ — 进程使用的CPU时间总计,单位1/100秒
COMMAND — 进程名称(命令名/命令行)
top按照cpu/内存降序排列
shift+f可以进入选择,s选中一行然后esc退出选择即可
关于linux的buffer/cache内存
total=used+free+buffers/cached
在 Linux 的内存管理中,这里的buffer 指 Linux 内存的:Buffer cache。这里的 cache 指 Linux 内存中的:Page cache。翻译成中文可以叫做缓冲区缓存和页面缓存。在历史上,它们一个(buffer)被用来当成对 io 设备写的缓存,而另一个(cache)被用来当作对 io 设备的读缓存,这里的 io 设备,主要指的是块设备文件和文件系统上的普通文件。但是现在,它们的意义已经不一样了。在当前的内核中,page cache 顾名思义就是针对内存页的缓存,说白了就是,如果有内存是以 page 进行分配管理的,都可以使用 page cache 作为其缓存来管理使用。当然,不是所有的内存都是以页page进行管理的,也有很多是针对块block进行管理的,这部分内存使用如果要用到 cache 功能,则都集中到 buffer cache 中来使用。(从这个角度出发,是不是 buffer cache 改名叫做 block cache 更好?)然而,也不是所有块block都有固定长度,系统上块的长度主要是根据所使用的块设备决定的,而页长度在 X86 上无论是32位还是64位都是 4k。
明白了这两套缓存系统的区别,就可以理解它们究竟都可以用来做什么了。
什么是 page cache
Page cache 主要用来作为文件系统上的文件数据的缓存来用,尤其是针对当进程对文件有 read/write 操作的时候。如果你仔细想想的话,作为可以映射文件到内存的系统调用:mmap 是不是很自然的也应该用到 page cache?在当前的系统实现里, page cache 也被作为其它文件类型的缓存设备来用,所以事实上 page cache 也负责了大部分的块设备文件的缓存工作。
什么是 buffer cache
Buffer cache 则主要是设计用来在系统对块设备进行读写的时候,对块进行数据缓存的系统来使用。这意味着某些对块的操作会使用 buffer cache 进行缓存,比如我们在格式化文件系统的时候。一般情况下两个缓存系统是一起配合使用的,比如当我们对一个文件进行写操作的时候,page cache 的内容会被改变,而 buffer cache 则可以用来将 page 标记为不同的缓冲区,并记录是哪一个缓冲区被修改了。这样,内核在后续执行脏数据的回写writeback时,就不用将整个 page 写回,而只需要写回修改的部分即可。
块设备也就是存储以“块”为单位数据的设备,比较典型的如磁盘设备、光盘或者u盘
jmap命令
首先用jps命令获取想分析的java进程pid
这里展示的实际上是运行的main class类名
可以用jps -l展示全类名
jmap -histo:live 785801 | more
即可降序排列展示哪些类的对象占用内存过多.
[C is a char[]
[S is a short[]
[I is a int[]
[B is a byte[]
[[I is a int[][]
如果报错
Unable to open socket file: target process not responding or HotSpot VM not loaded
可能是由于此进程不是当前用户启动的,可以切换用户再运行此命令
jvm运行时会生成一个目录hsperfdata_USER是启动java进程的用户),在linux中默认是/tmp。目录下会有些pid文件,存放jvm进程信息。
jps、jstack等工具读取/tmp/hsperfdata_$USER下的pid文件获取连接信息。
-heap参数可以打印java内存各部分占用空间信息
我们知道从1.8开始不再有永久代,而是改为Metaspace.从图中我们也可以看到这一点
Heap Configuration
MinHeapFreeRatio = 40
空闲堆空间的最小百分比,计算公式为:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的区间为0到100,默认值为 40。如果HeapFreeRatio < MinHeapFreeRatio,则需要进行堆扩容,扩容的时机应该在每次垃圾回收之后。
MaxHeapFreeRatio = 70
空闲堆空间的最大百分比,计算公式为:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的区间为0到100,默认值为 70。如果HeapFreeRatio > MaxHeapFreeRatio,则需要进行堆缩容,缩容的时机应该在每次垃圾回收之后。
MaxHeapSize = 4294967296 (4096.0MB)
JVM 堆空间允许的最大值。
NewSize = 1363144 (1.2999954223632812MB)
JVM 新生代堆空间的默认值。
MaxNewSize = 2576351232 (2457.0MB)
JVM 新生代堆空间允许的最大值。
OldSize = 5452592 (5.1999969482421875MB)
JVM 老年代堆空间的默认值。
NewRatio = 2
新生代(2个Survivor区和Eden区 )与老年代(不包括永久区)的堆空间比值,表示新生代:老年代=1:2。
SurvivorRatio = 8
两个Survivor区和Eden区的堆空间比值为 8,表示 S0 : S1 :Eden = 1:1:8。
MetaspaceSize = 21807104 (20.796875MB)
JVM 元空间的默认值。
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
JVM 元空间允许的最大值。
下面的解释基于jvm11才有的G1收集器。
G1HeapRegionSize = 1048576 (1.0MB)
在使用 G1 垃圾回收算法时,JVM 会将 Heap 空间分隔为若干个 Region,该参数用来指定每个 Region 空间的大小。
三、Heap Usage
G1 Heap:
regions = 4096
capacity = 4294967296 (4096.0MB)
used = 5242880 (5.0MB)
free = 4289724416 (4091.0MB)
0.1220703125% used
G1 的 Heap 使用情况,该 Heap 包含 4096 个 Region,结合上文每个 RegionSize=1M,因此 Capacity = Regions * RegionSize = 4096 * 1M = 4096M,已使用空间为 5M,空闲空间为 4091M,使用率为 0.12%。
G1 Young Generation:
Eden Space:
regions = 6
capacity = 27262976 (26.0MB)
used = 6291456 (6.0MB)
free = 20971520 (20.0MB)
23.076923076923077% used
G1 的 Eden 区的使用情况,总共使用了 6 个 Region,结合上文每个 RegionSize=1M,因此 Used = Regions * RegionSize = 6 * 1M = 6M,Capacity=26M表名当前 Eden 空间分配了 26 个 Region,使用率为 23%。
G1 Young Generation:
Survivor Space:
regions = 0
capacity = 0 (0.0MB)
used = 0 (0.0MB)
free = 0 (0.0MB)
0.0% used
G1 的 Survivor 区的使用情况,同 Eden区。
G1 Old Generation:
regions = 0
capacity = 241172480 (230.0MB)
used = 0 (0.0MB)
free = 241172480 (230.0MB)
0.0% used
查看jvm dump
jmap -dump:live,format=b,file=heap.hprof <pid>
生成的文件名为heap.hprof
jhat heap.hprof
将会启动一个web server</pid>
访问http://10.227.26.77:7000/
即可查看分析结果
更常用的可能是Eclipse Memory Analyzer(MAT)