Linux使用ELF格式作为可执行文件的组织形式。
ELF有自己的详细规则。
对应的数据结构如下:
/*
* An entry in the ELF program header table.
* This describes a single segment of the executable.
*/
typedef struct {
unsigned int type;
unsigned int offset;
unsigned int vaddr;
unsigned int paddr;
unsigned int fileSize;
unsigned int memSize;
unsigned int flags;
unsigned int alignment;
} programHeader;
/*
* Bits in flags field of programHeader.
* These describe memory permissions required by the segment.
*/
#define PF_R 0x4 /* Pages of segment are readable. */
#define PF_W 0x2 /* Pages of segment are writable. */
#define PF_X 0x1 /* Pages of segment are executable. */
/*
* A segment of an executable.
* It specifies a region of the executable file to be loaded
* into memory.
*/
struct Exe_Segment {
ulong_t offsetInFile; /* Offset of segment in executable file */
ulong_t lengthInFile; /* Length of segment data in executable file */
ulong_t startAddress; /* Start address of segment in user memory */
ulong_t sizeInMemory; /* Size of segment in memory */
int protFlags; /* VM protection flags; combination of VM_READ,VM_WRITE,VM_EXEC */
};
/*
* Maximum number of executable segments we allow.
* Normally, we only need a code segment and a data segment.
* Recent versions of gcc (3.2.3) seem to produce 3 segments.
*/
#define EXE_MAX_SEGMENTS 3
/*
* A struct concisely representing all information needed to
* load an execute an executable.
*/
struct Exe_Format {
struct Exe_Segment segmentList[EXE_MAX_SEGMENTS]; /* Definition of segments */
int numSegments; /* Number of segments contained in the executable */
ulong_t entryAddr; /* Code entry point address */
};
操作系统根据规则解析文件,分别设置代码段和数据段和程序入口地址。
设置时结合内存分配、内存分配需要虚拟地址和预留栈空间。
// program header table, + offset from elfHead
programHeader *proHeader=(programHeader *)(exeFileData + elfHead->phoff);
// program entry, that is code entry
exeFormat->entryAddr = elfHead->entry;
// program segments number
exeFormat->numSegments = elfHead->phnum;
Print("the number of entries in the program header table: %d /n", elfHead->phnum);
for(k=0; k<elfHead->phnum; k++)
{
// proHeader offset
exeFormat->segmentList[k].offsetInFile = proHeader->offset;
// fileSize
exeFormat->segmentList[k].lengthInFile = proHeader->fileSize;
// virtual address
exeFormat->segmentList[k].startAddress = proHeader->vaddr;
// memory size
exeFormat->segmentList[k].sizeInMemory = proHeader->memSize;
// flag
exeFormat->segmentList[k].protFlags = proHeader->flags;
proHeader++;
}
return 0;
}
最后跳转到程序入口进行执行,执行后返回。
程序参数为代码段、数据段、入口。
push ds
push es
mov ds, ax
mov es, ax
;; push KERNEL_CS/EIP so that we return here
;; after running the program
push dword KERNEL_CS
push dword .backhere
;; now make the inter-selector jump
;; we land in different cs/ds descriptors
push ebx
push ecx
retf
.backhere:
pop es
pop ds
ret