GNU gold链接器 - target.cc 实现特定目标架构的支持

一、Target::do_is_local_label_name(const char* name) const

  1. object.cc 中 调用target().is_local_label_name(name)
cpp 复制代码
这段代码是在链接器中用于决定是否应该丢弃本地符号的部分。它包含了一些逻辑,以便在满足特定条件时丢弃本地符号。下面是关键部分的解释:
条件判断:这段代码执行了一系列条件检查,以确定是否应该丢弃本地符号。判断是基于以下条件:
discard_locals 变量控制是否应该丢弃本地符号,如果为真,将会丢弃。
discard_sec_merge 变量控制是否应该丢弃合并部分(merge sections)中的本地符号。
is_ordinary 表示符号是否为普通符号。
out_section_offsets[shndx] 用于检查符号所属的输出节(section)是否为无效地址(invalid_address)。
符号属性检查:代码接着检查符号的一些属性:
sym.get_st_type() != elfcpp::STT_FILE 用于排除文件类型符号。
!lv.needs_output_dynsym_entry() 确保不需要动态符号表入口。
lv.may_be_discarded_from_output_symtab() 检查是否可以从输出符号表中丢弃。
本地标签名检查:最后,通过调用 parameters->target().is_local_label_name(name) 来检查符号名称是否是本地标签名。
如果是本地标签名,那么这个符号将被设置为不输出到符号表中,即被丢弃。
   */
      if ((discard_locals
	   || (discard_sec_merge
	       && is_ordinary
	       && out_section_offsets[shndx] == invalid_address))
	  && sym.get_st_type() != elfcpp::STT_FILE
	  && !lv.needs_output_dynsym_entry()
	  && lv.may_be_discarded_from_output_symtab()
	  && parameters->target().is_local_label_name(name))
	{
	  lv.set_no_output_symtab_entry();
	  continue;
	}
  1. target.h中调用类的成员函数 ,do_is_local_label_name是个虚函数,可以在别的地方实现
cpp 复制代码
// Return whether NAME is a local label name.  This is used to implement the
  // --discard-locals options.
  bool
  is_local_label_name(const char* name) const
  { return this->do_is_local_label_name(name); }
  1. 在target.cc中实现这个函数,目的就是判断符号是不是局部符号
cpp 复制代码
//  用于判断一个符号名是否是局部标签名
bool
Target::do_is_local_label_name(const char* name) const
{
  
  // Normal local symbols start with ``.L''.
  if (name[0] == '.' && name[1] == 'L')
    return true;

  // At least some SVR4 compilers (e.g., UnixWare 2.1 cc) generate
  // DWARF debugging symbols starting with ``..''.
  if (name[0] == '.' && name[1] == '.')
    return true;

  // gcc will sometimes generate symbols beginning with ``_.L_'' when
  // emitting DWARF debugging output.  I suspect this is actually a
  // small bug in gcc (it calls ASM_OUTPUT_LABEL when it should call
  // ASM_GENERATE_INTERNAL_LABEL, and this causes the leading
  // underscore to be emitted on some ELF targets).  For ease of use,
  // we treat such symbols as local.
  if (name[0] == '_' && name[1] == '.' && name[2] == 'L' && name[3] == '_')
    return true;

  return false;
}

二、Object* Target::do_make_elf_object

用于根据不同的目标平台和体系结构,选择合适的函数来创建 ELF 对象。这段代码的目的是根据不同的目标平台和体系结构选择正确的函数版本,以创建符合目标平台特性的 ELF 对象。

cpp 复制代码
// Make an ELF object called NAME by reading INPUT_FILE at OFFSET.  EHDR
// is the ELF header of the object.  There are four versions of this
// for different address sizes and endianities.
// LITTLE 位小端字节序 32 表示地址大小,
#ifdef HAVE_TARGET_32_LITTLE
Object*
Target::do_make_elf_object(const std::string& name, Input_file* input_file,
			   off_t offset, const elfcpp::Ehdr<32, false>& ehdr)
{
  return this->do_make_elf_object_implementation<32, false>(name, input_file,
							    offset, ehdr);
}
#endif

#ifdef HAVE_TARGET_32_BIG
Object*
Target::do_make_elf_object(const std::string& name, Input_file* input_file,
			   off_t offset, const elfcpp::Ehdr<32, true>& ehdr)
{
  return this->do_make_elf_object_implementation<32, true>(name, input_file,
							   offset, ehdr);
}
#endif

#ifdef HAVE_TARGET_64_LITTLE
Object*
Target::do_make_elf_object(const std::string& name, Input_file* input_file,
			   off_t offset, const elfcpp::Ehdr<64, false>& ehdr)
{
  return this->do_make_elf_object_implementation<64, false>(name, input_file,
							    offset, ehdr);
}
#endif

#ifdef HAVE_TARGET_64_BIG
Object*
Target::do_make_elf_object(const std::string& name, Input_file* input_file,
			   off_t offset, const elfcpp::Ehdr<64, true>& ehdr)
{
  return this->do_make_elf_object_implementation<64, true>(name, input_file,
							   offset, ehdr);
}
#endif

跟据目标平台地址大小和字节顺序,创建一个ELF对象

cpp 复制代码
// Implementations of methods Target::do_make_elf_object are almost identical
// except for the address sizes and endianities.  So we extract this
// into a template.
//  一个模板方法,用于创建一个 ELF 对象。这个方法的实现依赖于目标平台的地址大小和字节顺序。
template<int size, bool big_endian>
inline Object*
Target::do_make_elf_object_implementation(
    const std::string& name,
    Input_file* input_file,
    off_t offset,
    const elfcpp::Ehdr<size, big_endian>& ehdr)
 // name 是对象的名称,input_file 是输入文件对象,offset 是文件中 ELF 头的偏移,ehdr 是 ELF 头的信息。

{
  int et = ehdr.get_e_type(); // 获取 ELF 文件类型
  // ET_EXEC files are valid input for --just-symbols/-R,
  // and we treat them as relocatable objects.
  // elfcpp::ET_REL表示是可重定位对象文件,文件类型是 elfcpp::ET_EXEC 且输入文件是仅包含符号的情况(input_file->just_symbols()),则将其视为可重定位对象
  // ET_REL (1): 表示可重定位文件     ET_EXEC (2): 表示可执行文件     ET_DYN (3): 表示共享对象文件  
  if (et == elfcpp::ET_REL
      || (et == elfcpp::ET_EXEC && input_file->just_symbols()))
    {
      Sized_relobj_file<size, big_endian>* obj =
	new Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr); 
  //  Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr) 是模板类的实例化
      obj->setup();
      return obj;
    }
  else if (et == elfcpp::ET_DYN)
    {
      Sized_dynobj<size, big_endian>* obj =
	new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr);
      obj->setup();
      return obj;
    }
  else
    {
      gold_error(_("%s: unsupported ELF file type %d"),
		 name.c_str(), et);
      return NULL;
    }
}

三、Output_section* Target::do_make_output_section

cpp 复制代码
// 用于创建输出段对象
Output_section*
Target::do_make_output_section(const char* name, elfcpp::Elf_Word type,
			       elfcpp::Elf_Xword flags)
// 段的名称 name、段的类型 type 和段的标志 flags。
{
  // 链接器中创建输出段对象,以便后续将链接结果存储在这些段中,这些段将成为生成的可执行文件或共享库的一部分。
  return new Output_section(name, type, flags);
}

四、 bool Target::do_is_call_to_non_split

用于确定重定位的时候是否对非分割函数调用

cpp 复制代码
// Default for whether a reloc is a call to a non-split function is
// whether the symbol is a function.
// 用于确定一个重定位是否是对非分割函数的调用
/*
  非分割函数" 指的是在程序的链接和执行过程中不会被拆分或分割成多个部分的函数。这意味着这些函数的定义是完整的,
  没有被分成多个代码段,它们可以被直接调用并执行,而不需要进一步的合并或组装。这与 "分割函数" 形成对比,
  分割函数通常被分成多个部分以实现某种特定的优化或节省内存的目的。
*/

bool
Target::do_is_call_to_non_split(const Symbol* sym, const unsigned char*,
				const unsigned char*, section_size_type) const
{
  return sym->type() == elfcpp::STT_FUNC;
  // 如果符号的类型是函数类型,那么函数返回 true,表示这个重定位是对非分割函数的调用;否则返回 false。
}

五、void Target::do_calls_non_split

用于出链接器涉及栈分割时,打印错误消息

cpp 复制代码
// Default conversion for -fsplit-stack is to give an error.
// 用于处理在链接器中涉及到栈分割(stack split)支持的情况
// 会设置 warned 为 false,并输出一条错误消息,指出链接器不包括栈分割支持,而栈分割是由链接对象(object)所需的
void
Target::do_calls_non_split(Relobj* object, unsigned int, section_offset_type,
			   section_size_type, const unsigned char*, size_t,
			   unsigned char*, section_size_type,
			   std::string*, std::string*) const
{
  static bool warned;
  // 一次性警告
  if (!warned)
    {
      gold_error(_("linker does not include stack split support "
		   "required by %s"),
		 object->name().c_str());
      warned = true;
    }
}

六、bool Target::match_view

这个函数通常用于检查视图中特定位置的字节数据是否与预期的字节数组匹配,以便进行一些数据匹配和校验操作。

cpp 复制代码
//  Return whether BYTES/LEN matches VIEW/VIEW_SIZE at OFFSET.
// 用于比较一个视图(view)中的字节数据是否与给定的字节数组匹配。
// 函数会检查是否在给定的视图(view)中的指定偏移(offset)处的一段字节与指定的字节数组(bytes)匹配。
/* 
视图"(View)通常是指对目标文件或可执行文件中的某一部分内存内容进行抽象和管理的一种数据结构或概念。
这些视图用于支持链接器的各种任务和操作,
包括但不限于以下几个方面:
内存管理: 视图可以用于管理目标文件或可执行文件在内存中的布局,包括各个段(sections)的起始地址、大小和属性。这有助于链接器将不同的目标文件组合成一个整体的可执行文件,保证各段在内存中的正确位置。
符号解析: 视图用于查找和定位目标文件中的符号表(symbol table),以便解析符号引用和符号定义。链接器使用视图来查找符号的地址,以便正确地建立符号引用关系。
重定位: 视图用于处理重定位信息,即在链接过程中对符号引用的地址进行修正。视图可以帮助链接器确定需要进行哪些重定位,以及如何修正目标文件中的地址,以适应最终的可执行文件。
段合并和填充: 在链接过程中,链接器可能需要将多个目标文件的段合并到一个输出段中,或者填充一些内存区域以满足对齐或布局的要求。视图用于管理这些操作,确保数据正确放置在内存中。
异常处理: 链接器可能需要生成或更新异常处理相关的信息,例如异常处理表(exception table)或调试信息,以便在程序出现异常时能够正确处理。
*/
bool
Target::match_view(const unsigned char* view, section_size_type view_size,
		   section_offset_type offset, const char* bytes,
		   size_t len) const
{
  if (offset + len > view_size)
    return false;
  return memcmp(view + offset, bytes, len) == 0;
}

七、 void Target::set_view_to_nop

它的作用是将指定的内存视图(view)中的一段内存区域设置为 NOP(No Operation)指令。这通常用于在链接过程中进行填充,以确保生成的可执行文件或共享库满足一些特定的要求,例如对齐或布局

cpp 复制代码
// Set the contents of a VIEW/VIEW_SIZE to nops starting at OFFSET
// for LEN bytes.

void
Target::set_view_to_nop(unsigned char* view, section_size_type view_size,
			section_offset_type offset, size_t len) const
// 方法用于将一段内存设置为 NOP 指令。这个方法主要用于处理链接过程中的填充问题。
{
  gold_assert(offset >= 0 && offset + len <= view_size); 
  // 检查链接器是否支持特点的代码填充方式 ,通过调用 this->has_code_fill() 来确定。
  if (!this->has_code_fill())
    memset(view + offset, 0, len);
  else
    {
      // 使用 this->code_fill(len) 来获取填充的具体数据
      std::string fill = this->code_fill(len);
      // 使用 memcpy 将填充数据复制到内存视图的指定位置,
      memcpy(view + offset, fill.data(), len);
    }
}

八、 void Target::do_plt_fde_location

PLT(Procedure Linkage Table)是一个存储在 plt 参数中的数据结构,用于动态链接和解析共享库中的函数。在链接器中,PLT 通常是一种输出数据,用于支持动态链接过程中的函数调用。

PLT 主要用于以下目的:

  1. Lazy Binding: PLT 支持延迟绑定,也称为懒绑定。在动态链接中,共享库中的函数不会在程序加载时立即绑定到调用点。相反,PLT 包含一组跳转指令,用于在第一次调用函数时将其绑定到相应的共享库函数。这可以减少程序启动时间。

  2. 解析外部符号: PLT 用于解析外部符号,即位于共享库中的函数。当程序调用共享库中的函数时,PLT 负责查找和绑定正确的函数。

  3. 异常处理: PLT 也与异常处理相关。在动态链接的情况下,异常处理需要知道 PLT 中函数的地址和大小,以便正确处理异常。

do_plt_fde_location**的目的是获取 PLT 的地址和大小,并将这些信息用于生成与 PLT 相关的 EH frame FDE。**这有助于确保程序在异常处理过程中能够正确识别和处理 PLT 中的函数。所以,PLT 是链接器的一个重要输出数据,用于支持程序的动态链接和异常处理。

cpp 复制代码
// Return address and size to plug into eh_frame FDEs associated with a PLT.
void
Target::do_plt_fde_location(const Output_data* plt, unsigned char*,
			    uint64_t* address, off_t* len) const
//  方法用于获取 PLT 的 FDE 地址和大小。这个方法主要用于处理异常处理框架(EH frame)的问题。

{
  *address = plt->address();
  *len = plt->data_size();
}

九、 void Sized_target<size, big_endian>::do_adjust_elf_header

用于调整 ELF 头的 EI_OSABI 字段,以处理目标平台的操作系统 ABI(Application Binary Interface)问题。ABI 定义了二进制程序与操作系统之间的接口规范,包括函数调用约定、系统调用方式、数据布局等。

cpp 复制代码
template<int size, bool big_endian>
void
Sized_target<size, big_endian>::do_adjust_elf_header(unsigned char* view,
						     int len)
{
  // 用于调整 ELF 头的 EI_OSABI 字段。这个方法主要用于处理目标平台的 OS ABI 问题。
  // 获取了当前目标平台的操作系统 ABI
  elfcpp::ELFOSABI osabi = this->osabi();
  if (osabi != elfcpp::ELFOSABI_NONE)
    {
      gold_assert(len == elfcpp::Elf_sizes<size>::ehdr_size);

      elfcpp::Ehdr<size, big_endian> ehdr(view);
      // 创建一个 ELF 头对象,根据给定的地址大小和字节顺序,该 ELF 头对象用于处理 ELF 文件头信息
      unsigned char e_ident[elfcpp::EI_NIDENT];
      // 创建了一个用于存储 ELF 头标识字段(e_ident)的数组
      memcpy(e_ident, ehdr.get_e_ident(), elfcpp::EI_NIDENT);
      // 将原始 ELF 头的标识字段复制到新的数组中
      e_ident[elfcpp::EI_OSABI] = osabi;
      // 新的标识字段中的 EI_OSABI 字段为操作系统 ABI 的值
      elfcpp::Ehdr_write<size, big_endian> oehdr(view);
      // 创建一个可写的 ELF 头对象,用于将修改后的标识字段写回原始 ELF 头的视图中
      oehdr.put_e_ident(e_ident);
    }
}

target.cc代码

cpp 复制代码
// target.cc -- target support for gold.

// Copyright (C) 2009-2017 Free Software Foundation, Inc.
// Written by Doug Kwan <dougkwan@google.com>.

// This file is part of gold.

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
// MA 02110-1301, USA.



/* 

这个文件是 GNU 的 Gold 链接器的一部分,用于实现特定目标平台的支持。以下是文件的详细解析:

*/

#include "gold.h"     //   包含了链接器的一些基本定义和全局函数
#include "elfcpp.h"   //   包含了处理 ELF (Executable and Linkable Format) 文件的类和函数。ELF 是一种常见的二进制文件格式,用于可执行文件、目标文件、共享库等
#include "dynobj.h"   //   定义了处理动态对象(如共享库)的类和函数
#include "symtab.h"   //   定义了处理符号表的类和函数。符号表是存储符号(如函数名、变量名)信息的数据结构  
#include "output.h"   //   定义了输出链接结果(如生成的可执行文件或共享库)的类和函数
#include "target.h"   //   定义了处理特定目标平台的类和函数。在编译和链接过程中,需要针对不同的目标平台(如 x86、ARM、MIPS 等)进行不同的处理

namespace gold
{

// Return whether NAME is a local label name.  This is used to implement the
// --discard-locals options and can be overridden by child classes to
// implement system-specific behaviour.  The logic here is the same as that
// in _bfd_elf_is_local_label_name().


//  用于判断一个符号名是否是局部标签名
bool
Target::do_is_local_label_name(const char* name) const
{
  
  // Normal local symbols start with ``.L''.
  if (name[0] == '.' && name[1] == 'L')
    return true;

  // At least some SVR4 compilers (e.g., UnixWare 2.1 cc) generate
  // DWARF debugging symbols starting with ``..''.
  if (name[0] == '.' && name[1] == '.')
    return true;

  // gcc will sometimes generate symbols beginning with ``_.L_'' when
  // emitting DWARF debugging output.  I suspect this is actually a
  // small bug in gcc (it calls ASM_OUTPUT_LABEL when it should call
  // ASM_GENERATE_INTERNAL_LABEL, and this causes the leading
  // underscore to be emitted on some ELF targets).  For ease of use,
  // we treat such symbols as local.
  if (name[0] == '_' && name[1] == '.' && name[2] == 'L' && name[3] == '_')
    return true;

  return false;
}

// Implementations of methods Target::do_make_elf_object are almost identical
// except for the address sizes and endianities.  So we extract this
// into a template.
//  一个模板方法,用于创建一个 ELF 对象。这个方法的实现依赖于目标平台的地址大小和字节顺序。
template<int size, bool big_endian>
inline Object*
Target::do_make_elf_object_implementation(
    const std::string& name,
    Input_file* input_file,
    off_t offset,
    const elfcpp::Ehdr<size, big_endian>& ehdr)
 // name 是对象的名称,input_file 是输入文件对象,offset 是文件中 ELF 头的偏移,ehdr 是 ELF 头的信息。

{
  int et = ehdr.get_e_type(); // 获取 ELF 文件类型
  // ET_EXEC files are valid input for --just-symbols/-R,
  // and we treat them as relocatable objects.
  // elfcpp::ET_REL表示是可重定位对象文件,文件类型是 elfcpp::ET_EXEC 且输入文件是仅包含符号的情况(input_file->just_symbols()),则将其视为可重定位对象
  // ET_REL (1): 表示可重定位文件     ET_EXEC (2): 表示可执行文件     ET_DYN (3): 表示共享对象文件  
  if (et == elfcpp::ET_REL
      || (et == elfcpp::ET_EXEC && input_file->just_symbols()))
    {
      Sized_relobj_file<size, big_endian>* obj =
	new Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr); 
  //  Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr) 是模板类的实例化
      obj->setup();
      return obj;
    }
  else if (et == elfcpp::ET_DYN)
    {
      Sized_dynobj<size, big_endian>* obj =
	new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr);
      obj->setup();
      return obj;
    }
  else
    {
      gold_error(_("%s: unsupported ELF file type %d"),
		 name.c_str(), et);
      return NULL;
    }
}

// Make an ELF object called NAME by reading INPUT_FILE at OFFSET.  EHDR
// is the ELF header of the object.  There are four versions of this
// for different address sizes and endianities.
// LITTLE 位小端字节序 32 表示地址大小,
#ifdef HAVE_TARGET_32_LITTLE
Object*
Target::do_make_elf_object(const std::string& name, Input_file* input_file,
			   off_t offset, const elfcpp::Ehdr<32, false>& ehdr)
{
  return this->do_make_elf_object_implementation<32, false>(name, input_file,
							    offset, ehdr);
}
#endif

#ifdef HAVE_TARGET_32_BIG
Object*
Target::do_make_elf_object(const std::string& name, Input_file* input_file,
			   off_t offset, const elfcpp::Ehdr<32, true>& ehdr)
{
  return this->do_make_elf_object_implementation<32, true>(name, input_file,
							   offset, ehdr);
}
#endif

#ifdef HAVE_TARGET_64_LITTLE
Object*
Target::do_make_elf_object(const std::string& name, Input_file* input_file,
			   off_t offset, const elfcpp::Ehdr<64, false>& ehdr)
{
  return this->do_make_elf_object_implementation<64, false>(name, input_file,
							    offset, ehdr);
}
#endif

#ifdef HAVE_TARGET_64_BIG
Object*
Target::do_make_elf_object(const std::string& name, Input_file* input_file,
			   off_t offset, const elfcpp::Ehdr<64, true>& ehdr)
{
  return this->do_make_elf_object_implementation<64, true>(name, input_file,
							   offset, ehdr);
}
#endif



// 用于创建输出段对象
Output_section*
Target::do_make_output_section(const char* name, elfcpp::Elf_Word type,
			       elfcpp::Elf_Xword flags)
// 段的名称 name、段的类型 type 和段的标志 flags。
{
  // 链接器中创建输出段对象,以便后续将链接结果存储在这些段中,这些段将成为生成的可执行文件或共享库的一部分。
  return new Output_section(name, type, flags);
}


// Default for whether a reloc is a call to a non-split function is
// whether the symbol is a function.
// 用于确定一个重定位是否是对非分割函数的调用
/*
  非分割函数" 指的是在程序的链接和执行过程中不会被拆分或分割成多个部分的函数。这意味着这些函数的定义是完整的,
  没有被分成多个代码段,它们可以被直接调用并执行,而不需要进一步的合并或组装。这与 "分割函数" 形成对比,
  分割函数通常被分成多个部分以实现某种特定的优化或节省内存的目的。
*/

bool
Target::do_is_call_to_non_split(const Symbol* sym, const unsigned char*,
				const unsigned char*, section_size_type) const
{
  return sym->type() == elfcpp::STT_FUNC;
  // 如果符号的类型是函数类型,那么函数返回 true,表示这个重定位是对非分割函数的调用;否则返回 false。
}


// Default conversion for -fsplit-stack is to give an error.
// 用于处理在链接器中涉及到栈分割(stack split)支持的情况
// 会设置 warned 为 false,并输出一条错误消息,指出链接器不包括栈分割支持,而栈分割是由链接对象(object)所需的
void
Target::do_calls_non_split(Relobj* object, unsigned int, section_offset_type,
			   section_size_type, const unsigned char*, size_t,
			   unsigned char*, section_size_type,
			   std::string*, std::string*) const
{
  static bool warned;
  // 一次性警告
  if (!warned)
    {
      gold_error(_("linker does not include stack split support "
		   "required by %s"),
		 object->name().c_str());
      warned = true;
    }
}

//  Return whether BYTES/LEN matches VIEW/VIEW_SIZE at OFFSET.
// 用于比较一个视图(view)中的字节数据是否与给定的字节数组匹配。
// 函数会检查是否在给定的视图(view)中的指定偏移(offset)处的一段字节与指定的字节数组(bytes)匹配。
/* 
视图"(View)通常是指对目标文件或可执行文件中的某一部分内存内容进行抽象和管理的一种数据结构或概念。
这些视图用于支持链接器的各种任务和操作,
包括但不限于以下几个方面:
内存管理: 视图可以用于管理目标文件或可执行文件在内存中的布局,包括各个段(sections)的起始地址、大小和属性。这有助于链接器将不同的目标文件组合成一个整体的可执行文件,保证各段在内存中的正确位置。
符号解析: 视图用于查找和定位目标文件中的符号表(symbol table),以便解析符号引用和符号定义。链接器使用视图来查找符号的地址,以便正确地建立符号引用关系。
重定位: 视图用于处理重定位信息,即在链接过程中对符号引用的地址进行修正。视图可以帮助链接器确定需要进行哪些重定位,以及如何修正目标文件中的地址,以适应最终的可执行文件。
段合并和填充: 在链接过程中,链接器可能需要将多个目标文件的段合并到一个输出段中,或者填充一些内存区域以满足对齐或布局的要求。视图用于管理这些操作,确保数据正确放置在内存中。
异常处理: 链接器可能需要生成或更新异常处理相关的信息,例如异常处理表(exception table)或调试信息,以便在程序出现异常时能够正确处理。
*/
bool
Target::match_view(const unsigned char* view, section_size_type view_size,
		   section_offset_type offset, const char* bytes,
		   size_t len) const
{
  if (offset + len > view_size)
    return false;
  return memcmp(view + offset, bytes, len) == 0;
}

// Set the contents of a VIEW/VIEW_SIZE to nops starting at OFFSET
// for LEN bytes.

void
Target::set_view_to_nop(unsigned char* view, section_size_type view_size,
			section_offset_type offset, size_t len) const
// 方法用于将一段内存设置为 NOP 指令。这个方法主要用于处理链接过程中的填充问题。
{
  gold_assert(offset >= 0 && offset + len <= view_size); 
  // 检查链接器是否支持特点的代码填充方式 ,通过调用 this->has_code_fill() 来确定。
  if (!this->has_code_fill())
    memset(view + offset, 0, len);
  else
    {
      // 使用 this->code_fill(len) 来获取填充的具体数据
      std::string fill = this->code_fill(len);
      // 使用 memcpy 将填充数据复制到内存视图的指定位置,
      memcpy(view + offset, fill.data(), len);
    }
}

// Return address and size to plug into eh_frame FDEs associated with a PLT.
void
Target::do_plt_fde_location(const Output_data* plt, unsigned char*,
			    uint64_t* address, off_t* len) const
//  方法用于获取 PLT 的 FDE 地址和大小。这个方法主要用于处理异常处理框架(EH frame)的问题。

{
  *address = plt->address();
  *len = plt->data_size();
}

// Class Sized_target.

// Set the EI_OSABI field of the ELF header if requested.

template<int size, bool big_endian>
void
Sized_target<size, big_endian>::do_adjust_elf_header(unsigned char* view,
						     int len)
{
  // 用于调整 ELF 头的 EI_OSABI 字段。这个方法主要用于处理目标平台的 OS ABI 问题。
  // 获取了当前目标平台的操作系统 ABI
  elfcpp::ELFOSABI osabi = this->osabi();
  if (osabi != elfcpp::ELFOSABI_NONE)
    {
      gold_assert(len == elfcpp::Elf_sizes<size>::ehdr_size);

      elfcpp::Ehdr<size, big_endian> ehdr(view);
      // 创建一个 ELF 头对象,根据给定的地址大小和字节顺序,该 ELF 头对象用于处理 ELF 文件头信息
      unsigned char e_ident[elfcpp::EI_NIDENT];
      // 创建了一个用于存储 ELF 头标识字段(e_ident)的数组
      memcpy(e_ident, ehdr.get_e_ident(), elfcpp::EI_NIDENT);
      // 将原始 ELF 头的标识字段复制到新的数组中
      e_ident[elfcpp::EI_OSABI] = osabi;
      // 新的标识字段中的 EI_OSABI 字段为操作系统 ABI 的值
      elfcpp::Ehdr_write<size, big_endian> oehdr(view);
      // 创建一个可写的 ELF 头对象,用于将修改后的标识字段写回原始 ELF 头的视图中
      oehdr.put_e_ident(e_ident);
    }
}

//  Sized_target 类的模板实例化。这些实例化分别对应了 32 位/64 位 和 大端/小端 的目标平台。

#ifdef HAVE_TARGET_32_LITTLE
template
class Sized_target<32, false>;
#endif

#ifdef HAVE_TARGET_32_BIG
template
class Sized_target<32, true>;
#endif

#ifdef HAVE_TARGET_64_LITTLE
template
class Sized_target<64, false>;
#endif

#ifdef HAVE_TARGET_64_BIG
template
class Sized_target<64, true>;
#endif

} // End namespace gold.
相关推荐
aherhuo1 分钟前
基于openEuler22.09部署OpenStack Yoga云平台(一)
linux·运维·服务器·openstack
WebDeveloper20014 分钟前
如何使用美国域名中心US Domain Center和WordPress创建商业网站
运维·服务器·css·网络·html
axxy200027 分钟前
leetcode之hot100---24两两交换链表中的节点(C++)
c++·leetcode·链表
檀越剑指大厂28 分钟前
【Linux系列】Shell 脚本中的条件判断:`[ ]`与`[[ ]]`的比较
linux·运维·服务器
chenziang134 分钟前
leetcode hot100 环形链表2
算法·leetcode·链表
若亦_Royi1 小时前
C++ 的大括号的用法合集
开发语言·c++
Captain823Jack2 小时前
nlp新词发现——浅析 TF·IDF
人工智能·python·深度学习·神经网络·算法·自然语言处理
2301_819287123 小时前
ce第六次作业
linux·运维·服务器·网络
Captain823Jack3 小时前
w04_nlp大模型训练·中文分词
人工智能·python·深度学习·神经网络·算法·自然语言处理·中文分词
武汉联从信息3 小时前
如何使用linux日志管理工具来管理oracle osb服务器日志文件?
linux·运维·服务器