汇编中函数定义及FUNC与NOTYPE标签差异的技术问询
Hey there! Let’s unpack your questions step by step, using the examples you provided to make things concrete.
First, let’s recap your assembly code (since it’s the foundation of your confusion):
section .data hello_string db "Hello World!", 0x0d, 0x0a hello_string_len equ $ - hello_string section .text global _start _start: mov eax, 4 ; eax <- 4, syscall number (print) But, never execute. mov ebx, 1 ; ebx <- 1, syscall argument1 (stdout) But, never execute. mov ecx, hello_string ; ecx <- exit_string, syscall argument2 (string ptr) But, never execute. mov edx, hello_string_len ; edx <- exit_string_len, syscall argument3 (string len) But, never execute. int 0x80; ; syscall But, never execute. mov eax, 1 ; eax <- 1, syscall number (exit) But, never execute. mov ebx, 0 ; ebx <- 0, syscall argument1 (return value) But, never execute. int 0x80; syscall But, never execute. ;nasm -felf32 hello.x86.s -o hello.o ;ld -m elf_i386 hello.o -o hello.out
And the readelf output for your assembly object file:
$readelf -s hello.o Symbol table '.symtab' contains 7 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 FILE LOCAL DEFAULT ABS hello.x86.s 2: 00000000 0 SECTION LOCAL DEFAULT 1 3: 00000000 0 SECTION LOCAL DEFAULT 2 4: 00000000 0 NOTYPE LOCAL DEFAULT 1 hello_string 5: 0000000e 0 NOTYPE LOCAL DEFAULT ABS hello_string_len 6: 00000000 0 NOTYPE GLOBAL DEFAULT 2 _start
For comparison, here’s the C function symbol output you referenced:
$readelf -s function.o | grep FUNC 3: 0000000000000000 18 FUNC GLOBAL DEFAULT 2 add 4: 0000000000000020 43 FUNC GLOBAL DEFAULT 2 main
Now let’s answer your questions:
1. What is a function in assembly?
In assembly, a "function" is just a named block of executable code marked by a label—there’s no built-in keyword or enforced structure like in C. Unlike C functions (which have standardized rules for call conventions, stack frames, and returns), assembly gives you full control:
- You define a label (like
_startin your code, or a custommy_add) to mark the start of the code block. - To use it as a function, you manually handle the call/return flow: use
callto jump to the label, set up parameters per your target’s calling convention (e.g., pushing args to the stack or using registers), and useretto jump back to the caller. - Your
_startlabel is the program’s entry point, but it’s structured like a function—it’s a code block that runs on program start—though it doesn’t return (since it calls theexitsyscall).
2. What’s the difference between a FUNC-type symbol and a NOTYPE label?
The gap comes down to how the toolchain interprets the symbol and the metadata attached to it:
- NOTYPE symbols (your assembly labels):
NASM defaults to marking all labels asNOTYPEbecause it doesn’t assume what the label points to—it could be executable code, a data value, or just an address marker. In your output, bothhello_string(a data pointer) and_start(a code entry point) areNOTYPEbecause NASM doesn’t automatically distinguish between them unless you explicitly tell it to. Notice theSizecolumn is 0 for these symbols too—NASM doesn’t calculate code block sizes by default. - FUNC-type symbols (C-generated):
When compiling C code, compilers like GCC mark function symbols asFUNCto signal to the linker and debuggers: "This is a block of executable code following standard function semantics". These symbols include extra metadata:- The
Sizecolumn shows the length of the function’s code (e.g., 18 bytes foradd, 43 formain). - The toolchain knows the symbol adheres to a specific calling convention (like System V AMD64 or i386), so it can handle stack frames, parameter passing, and returns correctly.
- The
If you want your assembly labels to show up as FUNC in the symbol table, you can explicitly specify the type in NASM using the function attribute when declaring global symbols:
global _start:function
This will mark _start as a FUNC type, just like C-generated functions.
内容的提问来源于stack exchange,提问作者JaeIL Ryu

