c++cvisual-c++linkerportable-executable

How Does the External Variable __ImageBase Work?


I understand what __ImageBase is and how to use it. __ImageBase is an external object of type IMAGE_DOS_HEADER, which is a structure representing the DOS header of a Portable Executable image. This variable is defined by the linker during the linking stage. When we retrieve the address of this variable, the address represents the base address of the image that the object file or the static library is linked into.

I also have a basic understanding of the compilation of a C/C++ program, as well as the fundamentals of the PE format and COFF object file format. However, I'm unsure about how the linker allocates the storage for this variable in the PE image in such a way that the __ImageBase variable lays out at the start of the image.

I'm curious about how it works internally, particularly from the perspective of the linking stage.

I've tried to look at the contents of the static library by using the dumpbin utility. But I couldn't manage to understand the output.

The source code of the static library I've written:

#pragma once

#ifndef MAIN_H
#define MAIN_H
 
#if __cplusplus
extern "C"
{
#endif

void print_image_base(void);

#if __cplusplus
}
#endif

#endif
#include <stdio.h>

#include <Windows.h>

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)

void print_image_base(void)
{
    printf("Image Base: %p\n", HINST_THISCOMPONENT);
}

Solution

  • As I understand it, the linker doesn't allocate any space for that structure, because it is already part of the executable and when running it is already mapped into memory.

    The linker just have to set up a constant symbol named __ImageBase with value 0x4000000 or whatever it is.

    Remember that the value of a symbol, from the linker point of view, is what in C you see as the address of that symbol. So when you read that struct from your C program you are actually reading from the memory address 0x4000000 (or whatever). Plus possible runtime relocations, of course.