abi:用json解析和对比linux平台下elf文件

引言

前段时间接触到了跟abi(注意不是api)相关的知识,简单的了解了一下elf文件格式并写了两个小样例。

具体的步骤为:

  • 修改readelf命令源码,增加其对h、S、l参数的json输出(修改后的代码过长就不放了)。
  • 可以不修改json源码,我用linux平台下的json-c写了一个自己的样例。
  • 对比获得的json文件,这里我用cJSON写了一个针对改后readelf的简单对比。

说实话对这方面兴趣真不大,发这篇博客当作一个因为个人原因可能即将烂尾的工程的纪念吧。

本篇博客代码和json文件下载:我的github仓库
elf文件格式简介:请参考《程序员的自我修养》第三章 目标文件里有什么
readelf命令简介:readelf (GNU Binary Utilities)
readelf命令源码:binutils/readelf.c
json文档:JSON中国 | JSON中文网

parse_elf_jsonc.c

#include <stdio.h>
#include <elf.h>
#include <fcntl.h>

#include <unistd.h>
#include <stddef.h>
#include "json-c/json.h"

#define my_err(format)      { \
                                    fprintf(stderr, "(File:%s, Line:%d) ", __FILE__, __LINE__); \
                                    perror(format); \
                                    exit(1); \
                            } \


void read_file_header(int fd, struct json_object *elf_json_object) {
  if (lseek(fd, 0, SEEK_SET) == -1)
    my_err("lseek error");
  Elf64_Ehdr file_header;
  if (read(fd, (void*)&file_header, sizeof(file_header)) == -1)
    my_err("read error");

  struct json_object *file_header_json_obeject = json_object_new_object();

  unsigned char magic_string[17] = {0};
  for (int i = 0; i < 16; i++)
    magic_string[i] = file_header.e_ident[i] + '0';
  json_object_object_add(file_header_json_obeject, "Magic", json_object_new_string(magic_string));
  json_object_object_add(file_header_json_obeject, "Type", json_object_new_int64(file_header.e_type));
  json_object_object_add(file_header_json_obeject, "Machine", json_object_new_int64(file_header.e_machine));
  json_object_object_add(file_header_json_obeject, "Version", json_object_new_int64(file_header.e_version));
  json_object_object_add(file_header_json_obeject, "Entry point address", json_object_new_int64(file_header.e_entry));
  json_object_object_add(file_header_json_obeject, "Start of program headers", json_object_new_int64(file_header.e_phoff));
  json_object_object_add(file_header_json_obeject, "Start of section headers", json_object_new_int64(file_header.e_shoff));
  json_object_object_add(file_header_json_obeject, "Flags", json_object_new_int64(file_header.e_flags));
  json_object_object_add(file_header_json_obeject, "Size of this header", json_object_new_int64(file_header.e_ehsize));
  json_object_object_add(file_header_json_obeject, "Size of program headers", json_object_new_int64(file_header.e_phentsize));
  json_object_object_add(file_header_json_obeject, "Number of program headers", json_object_new_int64(file_header.e_phnum));
  json_object_object_add(file_header_json_obeject, "Size of section header", json_object_new_int64(file_header.e_shentsize));
  json_object_object_add(file_header_json_obeject, "Number of section headers", json_object_new_int64(file_header.e_shnum));
  json_object_object_add(file_header_json_obeject, "Section header string table index", json_object_new_int64(file_header.e_shstrndx));

  json_object_object_add(elf_json_object, "File Header", file_header_json_obeject);
}

void read_program_header(int fd, struct json_object *elf_json_object) {
  if (lseek(fd, offsetof(Elf64_Ehdr, e_phoff), SEEK_SET) == -1)
    my_err("lseek error");
  Elf64_Off e_phoff;
  if (read(fd, (void*)&e_phoff, sizeof(e_phoff)) == -1)
  	my_err("read error");

  if (lseek(fd, offsetof(Elf64_Ehdr, e_phnum), SEEK_SET) == -1)
  	my_err("lseek error");
  Elf64_Half e_phnum;
  if (read(fd, (void*)&e_phnum, sizeof(e_phnum)) == -1)
    my_err("read error");

  if (lseek(fd, e_phoff, SEEK_SET) == -1)
  	my_err("lseek error");
  Elf64_Phdr program_headers[e_phnum];
  struct json_object *program_header_json_array = json_object_new_array();
  for (Elf64_Half i = 0; i < e_phnum; i++) {
	if (read(fd, (void *)&(program_headers[i]), sizeof(program_headers[i])) == -1)
		my_err("read error");

	struct json_object *program_header_json_array_i = json_object_new_object();

	json_object_object_add(program_header_json_array_i, "Type", json_object_new_int64(program_headers[i].p_type));
	json_object_object_add(program_header_json_array_i, "Offset", json_object_new_int64(program_headers[i].p_offset));
	json_object_object_add(program_header_json_array_i, "VirtAddr", json_object_new_int64(program_headers[i].p_vaddr));
	json_object_object_add(program_header_json_array_i, "PhysAddr", json_object_new_int64(program_headers[i].p_paddr));
	json_object_object_add(program_header_json_array_i, "FileSiz", json_object_new_int64(program_headers[i].p_filesz));
	json_object_object_add(program_header_json_array_i, "MemSiz", json_object_new_int64(program_headers[i].p_memsz));
	json_object_object_add(program_header_json_array_i, "Flg", json_object_new_int64(program_headers[i].p_flags));
	json_object_object_add(program_header_json_array_i, "Align", json_object_new_int64(program_headers[i].p_align));

	json_object_array_add(program_header_json_array, program_header_json_array_i);
  }
  
  json_object_object_add(elf_json_object, "Program Headers", program_header_json_array);
}

void read_section_header(int fd, struct json_object *elf_json_object) {
  if (lseek(fd, offsetof(Elf64_Ehdr, e_shoff), SEEK_SET) == -1)
  	my_err("lseek error");
  Elf64_Off e_shoff;
  if (read(fd, (void*)&e_shoff, sizeof(e_shoff)) == -1)
  	my_err("read error");
  
  if (lseek(fd, offsetof(Elf64_Ehdr, e_shnum), SEEK_SET) == -1)
  	my_err("lseek error");
  Elf64_Half e_shnum;
  if (read(fd, (void*)&e_shnum, sizeof(e_shnum)) == -1)
  	my_err("read error");

  if (lseek(fd, e_shoff, SEEK_SET) == -1)
  	my_err("lseek error");
  Elf64_Shdr section_headers[e_shnum];
  struct json_object *section_header_json_array = json_object_new_array();
  for (Elf64_Half i = 0; i < e_shnum; i++) {
	if (read(fd, (void *)&(section_headers[i]), sizeof(section_headers[i])) == -1)
	  my_err("read error");

	struct json_object *section_header_json_array_i = json_object_new_object();

	json_object_object_add(section_header_json_array_i, "Name", json_object_new_int64(section_headers[i].sh_name));
	json_object_object_add(section_header_json_array_i, "Type", json_object_new_int64(section_headers[i].sh_type));
	json_object_object_add(section_header_json_array_i, "Addr", json_object_new_int64(section_headers[i].sh_addr));
	json_object_object_add(section_header_json_array_i, "Off", json_object_new_int64(section_headers[i].sh_offset));
	json_object_object_add(section_header_json_array_i, "Size", json_object_new_int64(section_headers[i].sh_size));
	json_object_object_add(section_header_json_array_i, "ES", json_object_new_int64(section_headers[i].sh_entsize));
	json_object_object_add(section_header_json_array_i, "Flg", json_object_new_int64(section_headers[i].sh_flags));
	json_object_object_add(section_header_json_array_i, "Lk", json_object_new_int64(section_headers[i].sh_link));
	json_object_object_add(section_header_json_array_i, "Inf", json_object_new_int64(section_headers[i].sh_info));
	json_object_object_add(section_header_json_array_i, "Al", json_object_new_int64(section_headers[i].sh_addralign));

	json_object_array_add(section_header_json_array, section_header_json_array_i);
  }

  json_object_object_add(elf_json_object, "Program Headers", section_header_json_array);
}

int main(int argc, char **argv) {
  if (argc != 2) {
    printf("Usage : %s <filename>\n", argv[0]);
    return -1;
  }

  int fd = open(argv[1], O_RDONLY);
//  int fd = open("/home/nigo/桌面/try", O_RDONLY);
  if (fd == -1)
    my_err("open error");
  struct json_object *elf_json_object = json_object_new_object();

  read_file_header(fd, elf_json_object);
  read_program_header(fd, elf_json_object);
  read_section_header(fd, elf_json_object);

  printf("%s\n", json_object_to_json_string(elf_json_object));
  json_object_put(elf_json_object);
  if (close(fd) == -1)
    my_err("close error");
  return 0;
}

compare_elf_cJSON.cpp

// c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <cjson/cJSON.h>
#include <sys/stat.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
// cpp
#include <map>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>

using namespace std;

#define my_err(format)      { \
                                    fprintf(stderr, "(File:%s, Line:%d) ", __FILE__, __LINE__); \
                                    perror(format); \
                                    exit(1); \
                            } \

enum move_type {
  move_none,
  move_down,
  move_right,
  move_diagonal
};

map<move_type, const char*> color = {
	{move_down, "\033[32m"},
	{move_right, "\033[31m"},
	{move_diagonal, "\033[39m"},
	{move_none, "\033[0m"}
};

class Point {
  friend move_type compare(const Point &prev_point, const Point &point);
 public:
  Point(int x, int y) {
	this->x = x;
	this->y = y;
  }

  bool operator==(const Point &point) const {
	return this->x == point.x && this->y == point.y;
  }

  bool operator!=(const Point &point) const {
	return this->x != point.x || this->y != point.y;
  }

//  move_type operator>(const Point &point) {
//	if (this->x == point.x && this->y + 1 == point.y) {
//	  return move_down;
//	} else if (this->y == point.y && this->x + 1 == point.x) {
//	  return move_right;
//	} else if (this->x + 1 == point.x && this->y + 1 == point.y) {
//	  return move_diagonal;
//	}
//	return move_none;
//  }

 public:
  int x = 0;
  int y = 0;
};

// compare previous point and point
move_type compare(const Point &prev_point, const Point &point) {
  if (prev_point.x == point.x && prev_point.y + 1 == point.y) {
	return move_down;
  } else if (prev_point.y == point.y && prev_point.x + 1 == point.x) {
	return move_right;
  } else if (prev_point.x + 1 == point.x && prev_point.y + 1 == point.y) {
	return move_diagonal;
  }
  return move_none;
}

// myers' diff algorithm:
// T1 and T2 should overload operator[] and size
// for example, T1 and T2 can be
// string to compare character
// or vector<T> to compare T
template<typename T1, typename T2>
void myers_diff(const T1 &lhs, const T2 &rhs) {
  int N = lhs.size();
  int M = rhs.size();
  // map to save v of every d
  vector<map<int, int>> res_v;
  // map to save x by k
  // k -> x
  map<int, int> v;
  // base point : (0,-1)
  v[1] = 0;
  bool go_ahead = true;
  // d: step(excludes move of diagonal)
  // k: x - y
  for (int d = 0; d <= N + M && go_ahead; d++) {
	for (int k = -d; k <= d; k = k + 2) {
	  // delete is preferred
	  // so move right is preferred
	  // so bigger x is preferred
	  bool down = (k == -d || (k != d && v[k - 1] < v[k + 1]));
	  int k_prev = down ? k + 1 : k - 1;
	  // start point
	  int x_start = v[k_prev];
	  int y_start = x_start - k_prev;
	  // temp(mid) point
	  int x_tmp = down ? x_start : x_start + 1;
	  int y_tmp = x_tmp - k;
	  // end point
	  int x_end = x_tmp;
	  int y_end = y_tmp;
	  // move along the diagonal
	  while (x_end < N && y_end < M && lhs[x_end] == rhs[y_end]) {
		++x_end;
		++y_end;
	  }
	  v[k] = x_end;
	  // the result is found
	  if (x_end >= N && y_end >= M) {
		go_ahead = false;
		break;
	  }
	}
	// store the result
	res_v.push_back(v);
  }

  // the fallback
  vector<Point> path;
  Point point(lhs.size(), rhs.size());
  path.push_back(point);
  for (int d = res_v.size() - 1; point.x > 0 || point.y > 0; --d) {
	auto X = res_v[d];
	int k = point.x - point.y;
	int x_end = X[k];
	int y_end = point.x - k;
	// whether move along the diagonal
	while (x_end > 0 && y_end > 0 && x_end <= N && y_end <= M && lhs[x_end - 1] == rhs[y_end - 1]) {
	  --x_end;
	  --y_end;
	  path.push_back(Point(x_end, y_end));
	}

	bool down = (k == -d || (k != d && X[k - 1] < X[k + 1]));
	int k_prev = down ? k + 1 : k - 1;

	int x_start = X[k_prev];
	int y_start = x_start - k_prev;

	path.push_back(Point(x_start, y_start));

	point.x = x_start;
	point.y = y_start;
  }

  // pop (0,-1)
  path.pop_back();

  // compare and print
  if (path.size() >= 2) {
	int size = path.size();
	for (int i = size - 2; i >= 0; --i) {
	  Point prev_point = path[i + 1];
	  move_type type = compare(prev_point, path[i]);
	  switch (type) {
		case move_right: {
		  cout << color[move_right] << "-" << lhs[prev_point.x] << endl;
		  break;
		}
		case move_down: {
		  cout << color[move_down] << "+" << rhs[prev_point.y] << endl;
		  break;
		}
		case move_diagonal: {
		  cout << color[move_diagonal] << "=" << lhs[prev_point.x] << endl;
		  break;
		}
	  }
	}
  }
}

const int completed_option_nums = 2;
const double double_min_interval = 1e-6;

long int diff_item_num = 0, same_item_num = 0,
	added_item_num = 0, deleted_item_num = 0;

// split std::string by sep
// then return a vector<string> to store the result
vector<string> split_string(const string &str, const string &sep) {
  vector<string> res;
  string temp_s = str + sep;
  size_t pos = temp_s.find(sep);
  size_t size = temp_s.size();

  while (pos != string::npos) {
    // escape empty string(when sep is in the end)
	if (pos == 0)
	  break;
	string s = temp_s.substr(0, pos);
	res.push_back(s);
	temp_s = temp_s.substr(pos + 1, size);
	pos = temp_s.find(sep);
  }
  return res;
}

// parse from a filename to a json object
struct cJSON *cJSON_ParseFilename(const char *filename) {
  int fd = open(filename, O_RDONLY);
  if (fd == -1) my_err("open error");

  // obtain file size
  struct stat file_stat;
  if (fstat(fd, &file_stat) == -1) my_err("fstat error");
  __off_t size = file_stat.st_size;

  // read file by size
  char read_buf[size + 1];
  read(fd, (void *)read_buf, size);
  read_buf[size] = '\0';

  if (close(fd) == -1) my_err("close error");
  return cJSON_Parse(read_buf);
}

// judge buf whether is number key
int is_number_key(const char *buf) {
  // number Key won't be printed
  // atoi return 0 when error happens or string is "0"
  if ((atoi(buf) != 0) && (!(*buf == '0' && (buf[1] == '\0'))))
    return 0;
  return 1;
}

// compare normal cJSON Object and print
int cJSON_ComparePrintNormal(const struct cJSON *object1, const struct cJSON *object2, const char *dst_str) {
  const struct cJSON *json1, *json2;
  json1 = cJSON_GetObjectItem(object1, dst_str);
  json2 = cJSON_GetObjectItem(object2, dst_str);

  // avoid null cJSON object
  if ((!json1) || (!json2))
	return -1;

  // classify and compare
  if (cJSON_IsString(json1) && cJSON_IsString(json2)) {
	// for cJSON String Object
	const char *str1, *str2;
	str1 = cJSON_GetStringValue(json1);
	str2 = cJSON_GetStringValue(json2);

	if (strcmp(str1, str2) != 0) {
	  diff_item_num++;
	  if (is_number_key(dst_str))
		printf("%s : \n", dst_str);
	  cout << color[move_right] << "-" << str1 << endl << color[move_none];
	  cout << color[move_down] << "+" << str2 << endl << color[move_none];
	} else {
	  same_item_num++;
	}
  } else if (cJSON_IsNumber(json1) && cJSON_IsNumber(json2)) {
	// for cJSON Number object
	double d1, d2;
	d1 = cJSON_GetNumberValue(json1);
	d2 = cJSON_GetNumberValue(json2);

	if (fabs(d1 - d2) > double_min_interval) {
	  diff_item_num++;
	  if (is_number_key(dst_str))
		  printf("%s : \n", dst_str);
	  printf("%s-%.0lf\n%s+%.0lf\n%s",
		  color[move_right], d1, color[move_down], d2, color[move_none]);
	} else {
	  same_item_num++;
	}
  } else if (cJSON_IsObject(json1) && cJSON_IsObject(json2)) {
	// for cJSON Object like "Version":      {
	//                         "0":    1,
	//                         "1":    "(current)"
	//                 },
	char nk_buf[11];    // number key buffer (0, 1, ...)
	// INT_MAX is 2147483647
	sprintf(nk_buf, "%d", 0);
	while (cJSON_ComparePrintNormal(json1, json2, nk_buf) != -1) {
	  // 0 to 1 to ...
	  int nk = atoi(nk_buf);
	  sprintf(nk_buf, "%d", ++nk);
	}
  } else {
	printf("%s\n", dst_str);
	my_err("cJSON_ComparePrintNormal error");
  }
  return 0;
}

// compare option h then output
void compare_option_h(const char *filename1, const char *filename2) {
  struct cJSON *object1 = cJSON_ParseFilename(filename1);
  struct cJSON *object2 = cJSON_ParseFilename(filename2);
  struct cJSON *temp1_cJSON, *temp2_cJSON;
  temp1_cJSON = cJSON_GetObjectItem(object1, "ELF Header");
  temp2_cJSON = cJSON_GetObjectItem(object2, "ELF Header");

  printf("<option h> : \n");

  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "OS/ABI");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "ABI Version");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Type");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Machine");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Version");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Entry point address");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Start of program headers");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Start of section headers");

  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Size of this header");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Size of program headers");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Number of program headers");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Size of section headers");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Number of section headers");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Section header string table index");

  // just delete root cJSON object
  // otherwise cause double-free
  cJSON_Delete(object1);
  cJSON_Delete(object2);
}

// compare option S then output
void compare_option_S(const char *filename1, const char *filename2) {
  struct cJSON *object1 = cJSON_ParseFilename(filename1);
  struct cJSON *object2 = cJSON_ParseFilename(filename2);
  struct cJSON *temp1_cJSON, *temp2_cJSON;
  temp1_cJSON = cJSON_GetObjectItem(object1, "Section Headers");
  temp2_cJSON = cJSON_GetObjectItem(object2, "Section Headers");

  printf("<option S> \n");

  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "elf_header.e_shnum");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "elf_header.e_shoff");

  // compare Section Headers
  struct cJSON *temp1_array_cJSON, *temp2_array_cJSON;
  temp1_array_cJSON = cJSON_GetObjectItem(temp1_cJSON, "Section Headers");
  temp2_array_cJSON = cJSON_GetObjectItem(temp2_cJSON, "Section Headers");

  printf("Section Headers : \n");

  int array_size = cJSON_GetArraySize(temp1_array_cJSON);
  if (array_size != cJSON_GetArraySize(temp2_array_cJSON)) my_err("cJSON_GetArraySize error");

  for (int i = 0; i < array_size; ++i) {
	temp1_cJSON = cJSON_GetArrayItem(temp1_array_cJSON, i);
	temp2_cJSON = cJSON_GetArrayItem(temp2_array_cJSON, i);

	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Name");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Type");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Off");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Size");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "ES");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Flg");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Lk");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Inf");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Al");
  }

  cJSON_Delete(object1);
  cJSON_Delete(object2);
}

// compare option l then output
void compare_option_l(const char *filename1, const char *filename2) {
  struct cJSON *object1 = cJSON_ParseFilename(filename1);
  struct cJSON *object2 = cJSON_ParseFilename(filename2);
  struct cJSON *temp1_cJSON, *temp2_cJSON;
  temp1_cJSON = cJSON_GetObjectItem(object1, "Program Header");
  temp2_cJSON = cJSON_GetObjectItem(object2, "Program Header");

  printf("<option l> : \n");

  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Elf file type\"");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Entry point");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "elf_header.e_phnum");
  cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "starting at offset");

  // compare Section Headers
  struct cJSON *temp1_array_cJSON, *temp2_array_cJSON;
  temp1_array_cJSON = cJSON_GetObjectItem(temp1_cJSON, "Program Headers");
  temp2_array_cJSON = cJSON_GetObjectItem(temp2_cJSON, "Program Headers");

  printf("Program Headers : \n");

  int array_size = cJSON_GetArraySize(temp1_array_cJSON);
  if (array_size != cJSON_GetArraySize(temp2_array_cJSON)) my_err("cJSON_GetArraySize error");

  for (int i = 0; i < array_size; ++i) {
	temp1_cJSON = cJSON_GetArrayItem(temp1_array_cJSON, i);
	temp2_cJSON = cJSON_GetArrayItem(temp2_array_cJSON, i);
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Type");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Offset");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "VirtAddr");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "PhysAddr");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "FileSiz");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "MemSiz");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Flg");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "Align");
  }

  cJSON_Delete(object1);
  cJSON_Delete(object2);
}

// compare option r then output
void compare_option_r(const char *filename1, const char *filename2) {
  struct cJSON *object1 = cJSON_ParseFilename(filename1);
  struct cJSON *object2 = cJSON_ParseFilename(filename2);
  struct cJSON *temp1_cJSON, *temp2_cJSON;
  temp1_cJSON = cJSON_GetObjectItem(object1, "Relocation");
  temp2_cJSON = cJSON_GetObjectItem(object2, "Relocation");

  printf("<option r> : \n");

  // compare .rel.dyn
  struct cJSON *temp1_array_cJSON, *temp2_array_cJSON;
  temp1_array_cJSON = cJSON_GetObjectItem(temp1_cJSON, ".rel.dyn");
  temp2_array_cJSON = cJSON_GetObjectItem(temp2_cJSON, ".rel.dyn");

  printf(".rel.dyn : \n");

  int array_size = cJSON_GetArraySize(temp1_array_cJSON);
  if (array_size != cJSON_GetArraySize(temp2_array_cJSON)) my_err("cJSON_GetArraySize error");

  for (int i = 0; i < array_size; ++i) {
	temp1_cJSON = cJSON_GetArrayItem(temp1_array_cJSON, i);
	temp2_cJSON = cJSON_GetArrayItem(temp2_array_cJSON, i);
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "offset");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "info");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "type");
	cJSON_ComparePrintNormal(temp1_cJSON, temp2_cJSON, "sym->value");
  }

  cJSON_Delete(object1);
  cJSON_Delete(object2);
}

// print section cJSON object like
// {"section_name":{"0":"address","1":"value"}
void cJSON_PrintSection(struct cJSON *section_object, const char *section_name, move_type type) {
  printf("%s", color[type]);
  if (type == move_right) {
   printf("-");
  } else if (type == move_down) {
	printf("+");
  }
  printf("%s : \n", section_name);
  auto temp_cJSON = cJSON_GetObjectItem(section_object, section_name);
  printf("address : %s\n", cJSON_GetObjectItem(temp_cJSON, "0")->valuestring);

  auto vec_s = split_string(cJSON_GetObjectItem(temp_cJSON, "1")->valuestring, " ");
  int i;
  for (i = 0; i < vec_s.size(); i++) {
    if (i == 4) {
	  i = 0;
	  printf("\n");
	}
    cout << vec_s[i] << " ";
  }
  if (i != 4)
	printf("\n");

  printf("%s", color[move_none]);
}

// compare section cJSON object like
// {"section_name":{"0":"address","1":"value"}
// return 0 when same, 1 when different
int cJSON_ComparePrintSection(const struct cJSON *object1, const struct cJSON *object2, const char *dst_str) {
  int is_diff = 0;
  int is_sname_print = 0;
  const struct cJSON *json1, *json2;
  json1 = cJSON_GetObjectItem(object1, dst_str);
  json2 = cJSON_GetObjectItem(object2, dst_str);

  auto temp1_cJSON = cJSON_GetObjectItem(json1, "0");
  auto temp2_cJSON = cJSON_GetObjectItem(json2, "0");
  auto temp1_str = cJSON_GetStringValue(temp1_cJSON);
  auto temp2_str = cJSON_GetStringValue(temp2_cJSON);
  if (strcmp(temp1_str, temp2_str)) {
    is_diff = is_sname_print = 1;
    printf("%s : \n", dst_str);
    printf("address : \n");
	printf("%s-%s\n%s",
		color[move_right], temp1_str, color[move_none]);
	printf("%s+%s\n%s",
		color[move_down], temp2_str, color[move_none]);
  }

  temp1_cJSON = cJSON_GetObjectItem(json1, "1");
  temp2_cJSON = cJSON_GetObjectItem(json2, "1");
  temp1_str = cJSON_GetStringValue(temp1_cJSON);
  temp2_str = cJSON_GetStringValue(temp2_cJSON);
  if (strcmp(temp1_str, temp2_str)) {
    if (!is_sname_print) {
      is_sname_print = 1;
	  printf("%s : \n", dst_str);
    }
	printf("value : \n");
    // myers' diff
    myers_diff(split_string(temp1_str, " "), split_string(temp2_str, " "));
  }
  return is_diff;
}

// compare option x(improved) then output
// show all section headers
void compare_option_x_i(const char *filename1, const char *filename2) {
  struct cJSON *object1 = cJSON_ParseFilename(filename1);
  struct cJSON *object2 = cJSON_ParseFilename(filename2);

  printf("<option x(improved)> : \n");

  // compare Hex dump of section
  struct cJSON *temp1_array_cJSON, *temp2_array_cJSON;
  temp1_array_cJSON = cJSON_GetObjectItem(object1, "Hex dump of section");
  temp2_array_cJSON = cJSON_GetObjectItem(object2, "Hex dump of section");

  printf("Hex dump of section : \n");

  int array1_size = cJSON_GetArraySize(temp1_array_cJSON);
  int array2_size = cJSON_GetArraySize(temp2_array_cJSON);

  struct cJSON *temp1_cJSON, *temp2_cJSON;
  const char *section_name;
  // the same string(const char*) may have different address
  // so the key must be std::string
  map<string, struct cJSON *> s_map;

  // create a map of sections
  for (int i = 0; i < array1_size; ++i) {
	temp1_cJSON = cJSON_GetArrayItem(temp1_array_cJSON, i);
	section_name = temp1_cJSON->child->string;
	s_map[section_name] = temp1_cJSON;
  }

  // compare and update related variables
  for (int i = 0; i < array2_size; ++i) {
	temp2_cJSON = cJSON_GetArrayItem(temp2_array_cJSON, i);
	section_name = temp2_cJSON->child->string;
	if (s_map.find(section_name) != s_map.end()) {
	  // there are same sections in two file
	  // compare them
	  int is_diff = cJSON_ComparePrintSection(s_map[section_name], temp2_cJSON, section_name);
	  // after comparison, erase element
	  if (is_diff)
	    diff_item_num++;
	  else
	    same_item_num++;
	  s_map.erase(section_name);
	} else {
	  // otherwise output individually
	  char *temp_str;
	  cJSON_PrintSection(temp2_cJSON, section_name, move_down);
	  added_item_num++;
	}
  }
  // updating deleted_item_num must be here
  deleted_item_num += s_map.size();
  for (auto s : s_map) {
	cJSON_PrintSection(s.second, s.first.c_str(), move_right);
//	printf("%s-%s\n%s",
//		   color[move_right], cJSON_PrintUnformatted(s.second), color[move_none]);
  }

  cJSON_Delete(object1);
  cJSON_Delete(object2);
}

int main(int argc, char *argv[]) {
//  if (argc != completed_option_nums * 2 + 1) {
//	printf("Usage : %s <filename-h-1> <filename-h-2> "
//		   "<filename-S-1> <filename-S-2> "
//		   "<filename-l-1> <filename-l-2> "
//		   "<filename-r-1> <filename-r-2> "
//           "<filename-x-i-1> <filename-x-i-2> ", argv[0]);
//	return -1;
//  }

  compare_option_h("h1.json", "h2.json");
  compare_option_S("S1.json", "S2.json");
  compare_option_l("l1.json", "l2.json");
  compare_option_r("r1.json", "r2.json");
  compare_option_x_i("x1.json", "x2.json");

//  compare_option_h(argv[1], argv[2]);
//  compare_option_S(argv[3], argv[4]);
//  compare_option_l(argv[5], argv[6]);
//  compare_option_r(argv[7], argv[8]);
//  compare_option_x_i(argv[9], argv[10]);

  printf("different items number : %d\n", diff_item_num);
  printf("same items number : %d\n", same_item_num);
  printf("added items number : %d\n", added_item_num);
  printf("deleted items number : %d\n", deleted_item_num);

  return 0;
}

由于一开始使用c去写这个东西,但是最后需要map的支持,为了方便我还是改成了cpp,代码风格比较乱。
这里涉及到一个让人头大的算法——Myers’ diff查分算法,我看了两天才看下来。
这个算法写出来其实挺有意思的,具体可以参考:
Git 是怎样生成 diff 的:Myers 算法 - CJ Ting’s Blog
一个Go的实现
具体论文地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值