Slide 8 of L-L3 (Linking and Loading Part III) mentions how 32-bit x86 requires PC materialization within the Implementation of Shared Libraries. What does PC materialization mean and how can it be used to obtain the value of $eip?

PC materialization is a fancy name for a technique to obtain the value of the PC (program counter, on the x86 called the instruction pointer $eip) in a register so that it can be used for instructions that use relative addressing.

As an example, consider this code:

// pcmat.c
static int x;

int getx()
{
    return x;
}

when this code is compiled as a shared library, it may be loaded anywhere in the address space. However, the address where x is located will be at a known offset from where the function getx is located. So if the compiler could generate code that finds out where getx was loaded, it could use this value to generate code to access and return x. This is what it does.

When compiled with gcc -m32 -shared -fPIC -S pcmat.c, above code is compiled to:

	.file	"pcmat.c"
	.text
	.local	x
	.comm	x,4,4
	.globl	getx
	.type	getx, @function
getx:
	pushl	%ebp
	movl	%esp, %ebp
	call	__x86.get_pc_thunk.ax
	addl	$_GLOBAL_OFFSET_TABLE_, %eax
	movl	x@GOTOFF(%eax), %eax
	popl	%ebp
	ret
.LFE0:
	.size	getx, .-getx
	.section	.text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
	.globl	__x86.get_pc_thunk.ax
	.hidden	__x86.get_pc_thunk.ax
	.type	__x86.get_pc_thunk.ax, @function
__x86.get_pc_thunk.ax:
	movl	(%esp), %eax
	ret
	.ident	"GCC: (GNU) 8.3.1 20191121 (Red Hat 8.3.1-5)"
	.section	.note.GNU-stack,"",@progbits

The relevant parts here are:

	call	__x86.get_pc_thunk.ax

this calls a "function" that, upon return, has the value of $eip in $eax. Since a call instruction places the current value of the instruction pointer on the stack, this auto-generated function simply needs to read it from the top of the stack before returning:

__x86.get_pc_thunk.ax:
	movl	(%esp), %eax
	ret

Subsequently, after adjusting for the _GLOBAL_OFFSET_TABLE_ and the offset of x in that table, the value of x is read and returned:

	addl	$_GLOBAL_OFFSET_TABLE_, %eax
	movl	x@GOTOFF(%eax), %eax
	popl	%ebp
	ret

To avoid this indirection/complication, the x86_64 architecture introduced PC-relative addressing, so on this architecture, above code uses simply x(%rip) to achieve the same effect:

getx:
	pushq	%rbp
	movq	%rsp, %rbp
	movl	x(%rip), %eax
	popq	%rbp
	ret