Typically a modern assembler makes object code by translating assembly instruction into op codes, & by resolving symbolic names for memory locations and any other entities. The use of symbolic references is a crucial feature of assemblers, by saving tiring calculations and manual address updates after program modifications. Also most of the assemblers include macro facilities for performing textual substitution- for example, to produce common short sequences of instructions to run inline, rather of in a subroutine.
There are 2 types of assemblers based on how many passes through the source are required to generate the executable program. One-pass assemblers once go through the source code and suppose that all symbols will be defined before any instruction that references them. Two-pass assemblers (and multi-pass assemblers) first create a table with all unresolved symbols in the first pass and then use the 2nd pass to resolve these addresses. The benefit in one-pass assemblers is speed, which is not so much important as it once was with advances in computer speed and capabilities. The benefit of the two-pass assembler is that symbols may be defined anywhere in the program source. As a result, the program may be defined in a more logical and meaningful way. it makes two-pass assembler programs simpler to read and maintain.
Sophisticated high-level assemblers give language abstractions such as: