Note that the in example in the question, data is being disassembled as code. The .dynsym
section holds the dynamic linking symbol table and does not contain code.
Information such as the name and the virtual address of the first byte of a section is given in the section header of that section.
The section header is a structure stored in the section header table of an ELF binary.
In this case objdump
prints the virtual address 00000000004002c0
and the name .dynsym
because this is the information stored in the section header for that particular section (technically, section names are strings stored in the section header string table .shstrtab
, but this is not so important in this case). If there was no section header for this section or if there was no section header table present in the binary, objdump
would not be able to output this information.
There is no need for heuristics unless the section header table is not present in the binary. This is unusual, but has been observed to be the case in certain Linux ELF binaries (viruses) in order to thwart analysis since gdb
, objdump
, and other Binary File Descriptor-based utilities (thank you Igor Skochinsky for the correction) rely on information in the section header table to parse the binary.
Relevant documentation can be found in the System V ABI (generic) beginning in chapter 4 "Object Files" (page 44), as well as the manual page for ELF.
A section header table contains information describing the file’s sections. Every section has an entry in the table; each
entry gives information such as the section name, the section size, and so on.
More specifically:
An object file’s section header table lets one locate all the file’s sections. The section header table is an array of Elf32_Shdr
or
Elf64_Shdr
structures. A section header table index is a subscript into this array. The ELF header’s e_shoff
member gives the byte offset from the beginning of the file to the section header table; e_shnum
tells how many entries the section header table contains; e_shentsize
gives the size in bytes of each entry.
Additionally,
Sections contain all information in an object file, except the ELF header, the program header table, and the section header table. Moreover, object files’ sections satisfy several conditions.
Every section in an object file has exactly one section header describing it. Section headers may exist that do not have a section.
Each section occupies one contiguous (possibly empty) sequence of bytes within a file.
Sections in a file may not overlap. No byte in a file resides in more than one section.
An object file may have inactive space. The various headers and the sections might not ‘‘cover’’ every byte in an object file. The contents of the inactive data are unspecified.
Here is a high-level overview of what is involved in locating information contained in an ELF binary's individual section headers. The documentation cited is for ELF32 binaries, but the process is the same with ELF64 binaries.
The location of the section header table is given in the ELF header:

The ELF header is a struct and the offset of the section header table is stored in a member called e_shoff
:


$ readelf -h /bin/ls
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x404890
Start of program headers: 64 (bytes into file)
Start of section headers: 108288 (bytes into file) <---------- e_shoff
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 28
Section header string table index: 27
The section header table is usually located at the end of the binary following all of the sections:

As stated in the documentation and mentioned previously, every section has an entry in the section header table. Each entry is a structure that contains information about the section, including its name and its virtual address when the binary is mapped into memory. The name of the section is stored in a struct member called sh_name
, and the address information is stored in a struct member called sh_addr
:



Retrieving this information is just a matter of correctly parsing these structures within a given binary.
More information can be found here: