Sources/CEcho/ImageInspectionELF.c
// Callback used when iterating the shared objects loaded in this program's
// current memory. At the moment we only care about Swift's protocol
// conformances.
Callback used when iterating the shared objects loaded in this program's
current memory. At the moment we only care about Swift's protocol
conformances.
Callback used when iterating the shared objects loaded in this program ' s current memory . At the moment we only care about Swift ' s protocol conformances .
static int imageCallback(struct dl_phdr_info *info, size_t size, void *data) {
  if (!cacheSharedObject(info->dlpi_name)) {
    return 0;
  }
  void *so = NULL;
  const ElfW(Ehdr) *soHeader = NULL;
  FILE *file = fopen(info->dlpi_name, "rb");
  if (file == NULL) {
    return 0;
  }
  fseek(file, 0, SEEK_END);
  size_t fileSize = ftell(file);
  rewind(file);
  so = malloc(fileSize);
  soHeader = (ElfW(Ehdr) *)so;
  if (so == NULL) {
    return 0;
  }
  fread(so, fileSize, 1, file);
  fclose(file);
  const ElfW(Shdr) *sections = so + soHeader->e_shoff;
  const ElfW(Shdr) *stringTableSection = &sections[soHeader->e_shstrndx];
  const char *const stringTable = so + stringTableSection->sh_offset;
  for (int i = 0; i != soHeader->e_shnum; i += 1) {
    int sectionNameIdx = sections[i].sh_name;
    const char *sectionName = stringTable + sectionNameIdx;
    if (!strcmp(sectionName, "swift5_protocols")) {
      size_t sectionAddr = sections[i].sh_addr;
      const void *protos = (const void *)(info->dlpi_addr + sectionAddr);
      registerProtocols(protos, sections[i].sh_size);
      continue;
    }
    if (!strcmp(sectionName, "swift5_protocol_conformances")) {
      size_t sectionAddr = sections[i].sh_addr;
      const void *conformances = (const void *)(info->dlpi_addr + sectionAddr);
      registerProtocolConformances(conformances, sections[i].sh_size);
      continue;
    }
    if (!strcmp(sectionName, "swift5_type_metadata")) {
      size_t sectionAddr = sections[i].sh_addr;
      const void *types = (const void *)(info->dlpi_addr + sectionAddr);
      registerTypeMetadata(types, sections[i].sh_size);
      continue;
    }
  }
  free(so);
  return 0;
}
static int imageCallback ( struct dl_phdr_info * info , size_t size , void * data ) { if ( ! cacheSharedObject ( info -> dlpi_name ) ) { return 0 ; } void * so = NULL ; const ElfW ( Ehdr ) * soHeader = NULL ; FILE * file = fopen ( info -> dlpi_name , " " ) ; if ( file == NULL ) { return 0 ; } fseek ( file , 0 , SEEK_END ) ; size_t fileSize = ftell ( file ) ; rewind ( file ) ; so = malloc ( fileSize ) ; soHeader = ( ElfW ( Ehdr ) * ) so ; if ( so == NULL ) { return 0 ; } fread ( so , fileSize , 1 , file ) ; fclose ( file ) ; const ElfW ( Shdr ) * sections = so + soHeader -> e_shoff ; const ElfW ( Shdr ) * stringTableSection = & sections [ soHeader -> e_shstrndx ] ; const char * const stringTable = so + stringTableSection -> sh_offset ; for ( int i = 0 ; i != soHeader -> e_shnum ; i += 1 ) { int sectionNameIdx = sections [ i ] . sh_name ; const char * sectionName = stringTable + sectionNameIdx ; if ( ! strcmp ( sectionName , " " ) ) { size_t sectionAddr = sections [ i ] . sh_addr ; const void * protos = ( const void * ) ( info -> dlpi_addr + sectionAddr ) ; registerProtocols ( protos , sections [ i ] . sh_size ) ; continue ; } if ( ! strcmp ( sectionName , " " ) ) { size_t sectionAddr = sections [ i ] . sh_addr ; const void * conformances = ( const void * ) ( info -> dlpi_addr + sectionAddr ) ; registerProtocolConformances ( conformances , sections [ i ] . sh_size ) ; continue ; } if ( ! strcmp ( sectionName , " " ) ) { size_t sectionAddr = sections [ i ] . sh_addr ; const void * types = ( const void * ) ( info -> dlpi_addr + sectionAddr ) ; registerTypeMetadata ( types , sections [ i ] . sh_size ) ; continue ; } } free ( so ) ; return 0 ; }
Callback used when iterating the shared objects loaded in this program's
current memory.
Callback used when iterating the shared objects loaded in this program ' s current memory .
// Before doing anything here, check our cache to see if we've already added // this image's information. If not, this will cache our image name for future // iterations. // Our personal shared object in memory that isn't mapped into the process. // Open the full shared object file into memory so that we can inspect the // section header table. This is somewhat unfortunate, but is required // because ELF doesn't need the section header table at runtime, so it's // most likely stripped from the object currently in memory. // Figure out the size of the file. // Read the whole shared object and close our file handle now that we're done // with the file. // The section header table is located at the section header table offset // from the base address. // Our string table section, i.e. the section that provides all the names for // the object's sections, is the shstrdx'th index into the section header // table. // The actual string table is located at the section offset from the base // address // Loop through every section header looking for the one that describes any // Swift section that we may be interested in. // Our actual section name is in the string table at our index. // We found Swift's protocol section! // The actual conformances section within the program's memory is located // at the base address of this shared object's memory plus the vaddr // provided by the section header. // We found Swift's protocol conformances section! // The actual conformances section within the program's memory is located // at the base address of this shared object's memory plus the vaddr // provided by the section header. // We found Swift's type metadata section! // The actual conformances section within the program's memory is located // at the base address of this shared object's memory plus the vaddr // provided by the section header. // And finally, free the memory we allocated for our shared object :)